Mercurial > illumos > illumos-gate
changeset 11900:3d14975ff34e
PSARC 2010/041 USB CDC ECM driver
6922046 Support USB CDC ECM driver
author | Raymond Chen <Raymond.Chen@Sun.COM> |
---|---|
date | Fri, 12 Mar 2010 09:19:50 +0800 |
parents | b90a7691780e |
children | 4139b964b1ab |
files | usr/src/pkg/manifests/driver-network-usbecm.mf usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/usb/clients/usbecm/usbecm.c usr/src/uts/common/io/warlock/usbecm.wlcmd usr/src/uts/common/io/warlock/usbecm_with_usba.wlcmd usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h usr/src/uts/common/sys/usb/clients/usbecm/usbecm.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/usbecm/Makefile usr/src/uts/intel/warlock/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/usbecm/Makefile usr/src/uts/sparc/warlock/Makefile |
diffstat | 14 files changed, 4053 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkg/manifests/driver-network-usbecm.mf Fri Mar 12 09:19:50 2010 +0800 @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This package will install successfully into any zone, global or +# non-global. The files, directories, links, and hardlinks, however, +# will only be installed into the global zone. +# +<include hollow_zone_pkg> +set name=pkg.fmri value=pkg:/driver/network/usbecm@$(PKGVERS) +set name=pkg.description value="CDC ECM USB-to-Ethernet driver" +set name=pkg.summary value="CDC ECM USB-to-Ethernet driver" +set name=info.classification value=org.opensolaris.category.2008:Drivers/Networking +set name=variant.arch value=$(ARCH) +set name=variant.opensolaris.zone value=global value=nonglobal +dir path=kernel group=sys +dir path=kernel/drv group=sys +dir path=kernel/drv/$(ARCH64) group=sys +driver name=usbecm perms="* 0666 root sys" \ + alias=usb,class2.6.0 \ + alias=usbif,class2.6 \ + alias=usb430,a4a2 +file path=kernel/drv/$(ARCH64)/usbecm group=sys +$(i386_ONLY)file path=kernel/drv/usbecm group=sys +license cr_Sun license=cr_Sun +license lic_CDDL license=lic_CDDL
--- a/usr/src/uts/common/Makefile.files Thu Mar 11 16:01:06 2010 -0800 +++ b/usr/src/uts/common/Makefile.files Fri Mar 12 09:19:50 2010 +0800 @@ -771,6 +771,8 @@ USBFTDI_OBJS += usbser_uftdi.o uftdi_dsd.o +USBECM_OBJS += usbecm.o + WC_OBJS += wscons.o vcons.o VCONS_CONF_OBJS += vcons_conf.o
--- a/usr/src/uts/common/Makefile.rules Thu Mar 11 16:01:06 2010 -0800 +++ b/usr/src/uts/common/Makefile.rules Fri Mar 12 09:19:50 2010 +0800 @@ -1186,6 +1186,10 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/clients/usbecm/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/hcd/openhci/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -2365,6 +2369,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/clients/wusb_ca/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/clients/usbecm/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/hcd/openhci/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/usb/clients/usbecm/usbecm.c Fri Mar 12 09:19:50 2010 +0800 @@ -0,0 +1,3169 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * USB Ethernet Control Model + * + * USB-IF defines three ethernet network related specifications: EEM, + * ECM and NCM. This driver focuses specifically on ECM compatible + * devices. This kind of devices generally have one pair of bulk + * endpoints for in/out packet data and one interrupt endpoint for + * device notification. + * + * Devices which don't report ECM compatibility through descriptors but + * implement the ECM functions may also bind to this driver. This driver + * will try to find at least a bulk in endpoint and a bulk out endpoint + * in this case. If the non-compatible devices use vendor specific data + * format, this driver will not function. + * + * This driver is a normal USBA client driver. It's also a GLDv3 driver, + * which provides the necessary interfaces the GLDv3 framework requires. + * + */ + +#include <sys/types.h> +#include <sys/strsun.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/byteorder.h> +#include <sys/usb/usba/usbai_version.h> +#include <sys/usb/usba.h> +#include <sys/usb/usba/usba_types.h> +#include <sys/usb/clients/usbcdc/usb_cdc.h> +#include <sys/usb/clients/usbecm/usbecm.h> +#include <sys/mac_provider.h> +#include <sys/strsubr.h> +#include <sys/ethernet.h> +#include <sys/mac_ether.h> /* MAC_PLUGIN_IDENT_ETHER */ +#include <sys/random.h> /* random_get_bytes */ +#include <sys/sdt.h> /* sdt */ +#include <inet/nd.h> + +/* MAC callbacks */ +static int usbecm_m_stat(void *arg, uint_t stat, uint64_t *val); +static int usbecm_m_start(void *arg); +static void usbecm_m_stop(void *arg); +static int usbecm_m_unicst(void *arg, const uint8_t *macaddr); +static int usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m); +static int usbecm_m_promisc(void *arg, boolean_t on); +static void usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); +static mblk_t *usbecm_m_tx(void *arg, mblk_t *mp); +static int usbecm_m_getprop(void *arg, const char *pr_name, + mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf); +static int usbecm_m_setprop(void *arg, const char *pr_name, + mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); + +static int usbecm_usb_init(usbecm_state_t *ecmp); +static int usbecm_mac_init(usbecm_state_t *ecmp); +static int usbecm_mac_fini(usbecm_state_t *ecmp); + + +/* utils */ +static void generate_ether_addr(uint8_t *mac_addr); +static int usbecm_rx_start(usbecm_state_t *ecmp); + +static void usbecm_pipe_start_polling(usbecm_state_t *ecmp); +static void usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req); +static void usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req); +static void usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data); + +static int usbecm_reconnect_event_cb(dev_info_t *dip); +static int usbecm_disconnect_event_cb(dev_info_t *dip); + +static int usbecm_open_pipes(usbecm_state_t *ecmp); +static void usbecm_close_pipes(usbecm_state_t *ecmp); + +static int usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request, + uint16_t value, mblk_t **data, int len); +static int usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request, + uint16_t value, mblk_t **data); +static int usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data); +static int usbecm_send_zero_data(usbecm_state_t *ecmp); +static int usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, + uint32_t *stat_data); + +static int usbecm_create_pm_components(usbecm_state_t *ecmp); +static void usbecm_destroy_pm_components(usbecm_state_t *ecmp); +static int usbecm_power(dev_info_t *dip, int comp, int level); +static void usbecm_pm_set_busy(usbecm_state_t *ecmp); +static void usbecm_pm_set_idle(usbecm_state_t *ecmp); + +static int usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); +static int usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); + +static int usbecm_suspend(usbecm_state_t *ecmp); +static int usbecm_resume(usbecm_state_t *ecmp); +static int usbecm_restore_device_state(usbecm_state_t *ecmp); +static void usbecm_cleanup(usbecm_state_t *ecmp); + +/* Driver identification */ +static char usbecm_ident[] = "usbecm 1.0"; + +/* Global state pointer for managing per-device soft states */ +void *usbecm_statep; + +/* print levels */ +static uint_t usbecm_errlevel = USB_LOG_L3; +static uint_t usbecm_errmask = 0xffffffff; +static uint_t usbecm_instance_debug = (uint_t)-1; + +/* + * to prevent upper layers packet flood from exhausting system + * resources(USBA does not set limitation of requests on a pipe), + * we set a upper limit for the transfer queue length. + */ +static int usbecm_tx_max = 32; + +#define SUN_SP_VENDOR_ID 0x0430 +#define SUN_SP_PRODUCT_ID 0xa4a2 + +static uint8_t usbecm_broadcast[ETHERADDRL] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static usb_event_t usbecm_events = { + usbecm_disconnect_event_cb, + usbecm_reconnect_event_cb, + NULL, NULL +}; + +#define ECM_DS_OP_VALID(op) ((ecmp->ecm_ds_ops) && (ecmp->ecm_ds_ops->op)) + +/* + * MAC Call Back entries + */ +static mac_callbacks_t usbecm_m_callbacks = { + MC_IOCTL | MC_SETPROP | MC_GETPROP, + usbecm_m_stat, /* Get the value of a statistic */ + usbecm_m_start, /* Start the device */ + usbecm_m_stop, /* Stop the device */ + usbecm_m_promisc, /* Enable or disable promiscuous mode */ + usbecm_m_multicst, /* Enable or disable a multicast addr */ + usbecm_m_unicst, /* Set the unicast MAC address */ + usbecm_m_tx, /* Transmit a packet */ + NULL, + usbecm_m_ioctl, /* Process an unknown ioctl */ + NULL, /* mc_getcapab */ + NULL, /* mc_open */ + NULL, /* mc_close */ + usbecm_m_setprop, /* mc_setprop */ + usbecm_m_getprop, /* mc_getprop */ + NULL +}; + + +/* + * Module Loading Data & Entry Points + * Can't use DDI_DEFINE_STREAM_OPS, since it does + * not provide devo_power entry. + */ +static struct cb_ops cb_usbecm = { + 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 */ + NULL, /* cb_stream */ + D_MP, /* cb_flag */ + CB_REV, /* cb_rev */ + nodev, /* cb_aread */ + nodev, /* cb_awrite */ +}; + +static struct dev_ops usbecm_devops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + NULL, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + usbecm_attach, /* devo_attach */ + usbecm_detach, /* devo_detach */ + nodev, /* devo_reset */ + &(cb_usbecm), /* devo_cb_ops */ + (struct bus_ops *)NULL, /* devo_bus_ops */ + usbecm_power, /* devo_power */ + ddi_quiesce_not_needed /* devo_quiesce */ +}; + +static struct modldrv usbecm_modldrv = { + &mod_driverops, /* drv_modops */ + usbecm_ident, /* drv_linkinfo */ + &usbecm_devops /* drv_dev_ops */ +}; + +static struct modlinkage usbecm_ml = { + MODREV_1, /* ml_rev */ + &usbecm_modldrv, NULL /* ml_linkage */ +}; + + +/* + * Device operations + */ +/* + * Binding the driver to a device. + * + * Concurrency: Until usbecm_attach() returns with success, + * the only other entry point that can be executed is getinfo(). + * Thus no locking here yet. + */ +static int +usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + char strbuf[32]; + int instance; + int err; + usbecm_state_t *ecmp = NULL; + + switch (cmd) { + case DDI_ATTACH: + break; + + case DDI_RESUME: + ecmp = (usbecm_state_t *)ddi_get_soft_state(usbecm_statep, + ddi_get_instance(dip)); + + (void) usbecm_resume(ecmp); + + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + + instance = ddi_get_instance(dip); + + if (ddi_soft_state_zalloc(usbecm_statep, instance) == DDI_SUCCESS) { + ecmp = ddi_get_soft_state(usbecm_statep, instance); + } + if (ecmp == NULL) { + cmn_err(CE_WARN, "usbecm_attach: fail to get soft state"); + + return (DDI_FAILURE); + } + + ecmp->ecm_dip = dip; + + ecmp->ecm_lh = usb_alloc_log_hdl(ecmp->ecm_dip, "usbecm", + &usbecm_errlevel, &usbecm_errmask, &usbecm_instance_debug, 0); + + if (usbecm_usb_init(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_attach: failed to init usb"); + + goto fail; + } + + if (ECM_DS_OP_VALID(ecm_ds_init)) { + if (ecmp->ecm_ds_ops->ecm_ds_init(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_attach: failed to init DS"); + + goto fail; + } + } + + if (usbecm_mac_init(ecmp) != DDI_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_attach: failed to init mac"); + + goto fail; + } + ecmp->ecm_init_flags |= USBECM_INIT_MAC; + + /* + * Create minor node of type usb_net. Not necessary to create + * DDI_NT_NET since it's created in mac_register(). Otherwise, + * system will panic. + */ + (void) snprintf(strbuf, sizeof (strbuf), "usbecm%d", instance); + err = ddi_create_minor_node(dip, strbuf, S_IFCHR, + instance + 1, "usb_net", 0); + if (err != DDI_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "failed to create minor node"); + + goto fail; + } + + /* always busy. May change to a more precise PM in future */ + usbecm_pm_set_busy(ecmp); + + ddi_report_dev(dip); + + USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_attach: succeed!"); + + return (DDI_SUCCESS); + +fail: + USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_attach: Attach fail"); + + usbecm_cleanup(ecmp); + ddi_prop_remove_all(dip); + ddi_soft_state_free(usbecm_statep, instance); + + return (DDI_FAILURE); + +} + + +/* + * Detach the driver from a device. + * + * Concurrency: Will be called only after a successful attach + * (and not concurrently). + */ +static int +usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + usbecm_state_t *ecmp = NULL; + int instance; + + instance = ddi_get_instance(dip); + ecmp = ddi_get_soft_state(usbecm_statep, instance); + ASSERT(ecmp != NULL); + + USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_detach: entry "); + + switch (cmd) { + case DDI_DETACH: + break; + + case DDI_SUSPEND: + + return (usbecm_suspend(ecmp)); + + default: + return (DDI_FAILURE); + } + + usbecm_pm_set_idle(ecmp); + + if (ECM_DS_OP_VALID(ecm_ds_fini)) { + if (ecmp->ecm_ds_ops->ecm_ds_fini(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_detach: deinitialize DS fail!"); + + return (DDI_FAILURE); + } + } + + if (usbecm_mac_fini(ecmp) != 0) { + + return (DDI_FAILURE); + } + + USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_detach: exit"); + + usbecm_cleanup(ecmp); + ddi_soft_state_free(usbecm_statep, instance); + + return (DDI_SUCCESS); +} + + +/* + * Mac Call Back functions + */ + +/* + * Read device statistic information. + */ +static int +usbecm_m_stat(void *arg, uint_t stat, uint64_t *val) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + uint32_t stats; + int rval; + uint32_t fs; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_stat: entry, stat=%d", stat); + + /* + * Some of the stats are MII specific. We try to + * resolve all the statistics we understand. If + * the usb device can't provide it, return ENOTSUP. + */ + switch (stat) { + case MAC_STAT_IFSPEED: + /* return link speed */ + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_stat.es_downspeed) { + *val = ecmp->ecm_stat.es_downspeed; + } else { + *val = 10 * 1000000ull; /* set a default value */ + } + mutex_exit(&ecmp->ecm_mutex); + + return (0); + case ETHER_STAT_LINK_DUPLEX: + *val = LINK_DUPLEX_FULL; + + return (0); + + case ETHER_STAT_SQE_ERRORS: + *val = 0; + + return (0); + + /* Map MAC/Ether stats to ECM statistics */ + case MAC_STAT_NORCVBUF: + fs = ECM_RCV_NO_BUFFER; + + break; + case MAC_STAT_NOXMTBUF: + fs = ECM_XMIT_ERROR; + + break; + case MAC_STAT_IERRORS: + fs = ECM_RCV_ERROR; + + break; + case MAC_STAT_OERRORS: + fs = ECM_XMIT_ERROR; + + break; + case MAC_STAT_RBYTES: + fs = ECM_DIRECTED_BYTES_RCV; + + break; + case MAC_STAT_IPACKETS: + fs = ECM_RCV_OK; /* frames */ + + break; + case MAC_STAT_OBYTES: + fs = ECM_DIRECTED_BYTES_XMIT; + + break; + case MAC_STAT_OPACKETS: + fs = ECM_XMIT_OK; /* frames */ + + break; + case MAC_STAT_MULTIRCV: + fs = ECM_MULTICAST_FRAMES_RCV; + + break; + case MAC_STAT_BRDCSTRCV: + fs = ECM_BROADCAST_FRAMES_RCV; + + break; + case MAC_STAT_MULTIXMT: + fs = ECM_MULTICAST_FRAMES_XMIT; + + break; + case MAC_STAT_BRDCSTXMT: + fs = ECM_BROADCAST_FRAMES_XMIT; + + break; + case MAC_STAT_COLLISIONS: + fs = ECM_XMIT_MAX_COLLISIONS; + + break; + case MAC_STAT_OVERFLOWS: + fs = ECM_RCV_OVERRUN; + + break; + case MAC_STAT_UNDERFLOWS: + fs = ECM_XMIT_UNDERRUN; + + break; + case ETHER_STAT_FCS_ERRORS: + fs = ECM_RCV_CRC_ERROR; + + break; + case ETHER_STAT_ALIGN_ERRORS: + fs = ECM_RCV_ERROR_ALIGNMENT; + + break; + case ETHER_STAT_DEFER_XMTS: + fs = ECM_XMIT_DEFERRED; + + break; + case ETHER_STAT_FIRST_COLLISIONS: + fs = ECM_XMIT_ONE_COLLISION; + + break; + case ETHER_STAT_MULTI_COLLISIONS: + fs = ECM_XMIT_MORE_COLLISIONS; + + break; + case ETHER_STAT_TX_LATE_COLLISIONS: + fs = ECM_XMIT_LATE_COLLISIONS; + + break; + + default: + return (ENOTSUP); + } + + /* + * we need to access device to get required stats, + * so check device state first + */ + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_stat: device not ONLINE"); + + mutex_exit(&ecmp->ecm_mutex); + + return (EIO); + } + mutex_exit(&ecmp->ecm_mutex); + + rval = usbecm_get_statistics(ecmp, + ECM_STAT_SELECTOR(fs), &stats); + if (rval != USB_SUCCESS) { + mutex_enter(&ecmp->ecm_mutex); + switch (stat) { + case MAC_STAT_IERRORS: + *val = ecmp->ecm_stat.es_ierrors; + + break; + case MAC_STAT_OERRORS: + *val = ecmp->ecm_stat.es_oerrors; + + break; + case MAC_STAT_RBYTES: + *val = ecmp->ecm_stat.es_ibytes; + + break; + case MAC_STAT_IPACKETS: + *val = ecmp->ecm_stat.es_ipackets; + + break; + case MAC_STAT_OBYTES: + *val = ecmp->ecm_stat.es_obytes; + + break; + case MAC_STAT_OPACKETS: + *val = ecmp->ecm_stat.es_opackets; + + break; + case MAC_STAT_MULTIRCV: + *val = ecmp->ecm_stat.es_multircv; + + break; + case MAC_STAT_MULTIXMT: + *val = ecmp->ecm_stat.es_multixmt; + + break; + case MAC_STAT_BRDCSTRCV: + *val = ecmp->ecm_stat.es_brdcstrcv; + + break; + case MAC_STAT_BRDCSTXMT: + *val = ecmp->ecm_stat.es_brdcstxmt; + + break; + case ETHER_STAT_MACXMT_ERRORS: + *val = ecmp->ecm_stat.es_macxmt_err; + break; + default: + *val = 0; + + break; + } + mutex_exit(&ecmp->ecm_mutex); + } else { + *val = stats; + } + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_stat: end"); + + return (0); +} + + +/* + * Start the device: + * - Set proper altsettings of the data interface + * - Open status and data endpoints + * - Start status polling + * - Get bulk-in ep ready to receive data from ethernet + * + * Concurrency: Presumably fully concurrent, must lock. + */ +static int +usbecm_m_start(void *arg) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + int rval; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_start: entry"); + + (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_start: device not online"); + rval = ENODEV; + mutex_exit(&ecmp->ecm_mutex); + + goto fail; + } + mutex_exit(&ecmp->ecm_mutex); + + if (usbecm_open_pipes(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_start: open pipes fail"); + rval = EIO; + + goto fail; + } + + mutex_enter(&ecmp->ecm_mutex); + if (usbecm_rx_start(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_start: fail to start_rx"); + mutex_exit(&ecmp->ecm_mutex); + rval = EIO; + + goto fail; + } + ecmp->ecm_mac_state = USBECM_MAC_STARTED; + mutex_exit(&ecmp->ecm_mutex); + + /* set the device to receive all multicast/broadcast pkts */ + rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, + CDC_ECM_PKT_TYPE_DIRECTED | CDC_ECM_PKT_TYPE_ALL_MCAST | + CDC_ECM_PKT_TYPE_BCAST, NULL); + if (rval != USB_SUCCESS) { + USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_start: set packet filters fail," + " rval=%d, continue", rval); + } + + if (ECM_DS_OP_VALID(ecm_ds_start)) { + if (ecmp->ecm_ds_ops->ecm_ds_start(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_start: Can't start hardware"); + + goto fail; + } + } + + usb_release_access(ecmp->ecm_ser_acc); + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_start: end"); + + /* + * To mark the link as RUNNING. + * + * ECM spec doesn't provide a way for host to get the status + * of the physical link initiatively. Only the device can + * report the link state through interrupt endpoints. + */ + mac_link_update(ecmp->ecm_mh, LINK_STATE_UP); + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_stat.es_linkstate = LINK_STATE_UP; + mutex_exit(&ecmp->ecm_mutex); + + return (DDI_SUCCESS); +fail: + usb_release_access(ecmp->ecm_ser_acc); + + return (rval); +} + +/* + * Stop the device. + */ +static void +usbecm_m_stop(void *arg) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_stop: entry"); + + usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + if (ECM_DS_OP_VALID(ecm_ds_stop)) { + if (ecmp->ecm_ds_ops->ecm_ds_stop(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_stop: fail to stop hardware"); + } + } + + usbecm_close_pipes(ecmp); + usb_release_access(ecmp->ecm_ser_acc); + + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_mac_state = USBECM_MAC_STOPPED; + mutex_exit(&ecmp->ecm_mutex); + + mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN); + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN; + mutex_exit(&ecmp->ecm_mutex); + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_stop: end"); +} + +/* + * Change the MAC address of the device. + */ +/*ARGSUSED*/ +static int +usbecm_m_unicst(void *arg, const uint8_t *macaddr) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + uint16_t filter; + int rval; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_unicst: entry"); + + /* + * The device doesn't support to set a different MAC addr. + * Hence, it's not necessary to stop the device first if + * the mac addresses are identical. And we just set unicast + * filter only. + */ + if (bcmp(macaddr, ecmp->ecm_srcaddr, ETHERADDRL) != 0) { + USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_unicst: not supported to set a" + " different MAC addr"); + + return (DDI_FAILURE); + } + mutex_enter(&ecmp->ecm_mutex); + filter = ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_DIRECTED; + mutex_exit(&ecmp->ecm_mutex); + + usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, + filter, NULL); + usb_release_access(ecmp->ecm_ser_acc); + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_unicst: rval = %d", rval); + + /* some devices may not support this request, we just return success */ + return (DDI_SUCCESS); +} + +/* + * Enable/disable multicast. + */ +/*ARGSUSED*/ +static int +usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + uint16_t filter; + int rval = 0; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_multicst: entry"); + mutex_enter(&ecmp->ecm_mutex); + + /* + * To simplify the implementation, we support switching + * all multicast on/off feature only + */ + if (add == B_TRUE) { + ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_ALL_MCAST; + } else { + ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_ALL_MCAST; + } + filter = ecmp->ecm_pkt_flt; + mutex_exit(&ecmp->ecm_mutex); + + usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + if (ecmp->ecm_compatibility && + (ecmp->ecm_desc.wNumberMCFilters & 0x7F)) { + /* Device supports SetEthernetMulticastFilters request */ + rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, + filter, NULL); + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_multicst: rval = %d", rval); + } + usb_release_access(ecmp->ecm_ser_acc); + + /* some devices may not support this request, we just return success */ + return (DDI_SUCCESS); +} + +/* + * Enable/disable promiscuous mode. + */ +static int +usbecm_m_promisc(void *arg, boolean_t on) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + uint16_t filter; + int rval; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_promisc: entry"); + + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_promisc: device not ONLINE"); + mutex_exit(&ecmp->ecm_mutex); + + return (DDI_FAILURE); + } + + + if (on == B_TRUE) { + ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_PROMISC; + } else { + ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_PROMISC; + } + filter = ecmp->ecm_pkt_flt; + mutex_exit(&ecmp->ecm_mutex); + + usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT, + filter, NULL); + usb_release_access(ecmp->ecm_ser_acc); + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_promisc: rval=%d", rval); + + /* + * devices may not support this request, we just + * return success to let upper layer to do further + * operation. + */ + return (DDI_SUCCESS); +} + +/* + * IOCTL request: Does not do anything. Will be enhanced + * in future. + */ +static void +usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + struct iocblk *iocp; + int cmd; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_ioctl: entry"); + + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_ioctl: device not ONLINE"); + mutex_exit(&ecmp->ecm_mutex); + + miocnak(wq, mp, 0, EIO); + + return; + } + mutex_exit(&ecmp->ecm_mutex); + + iocp = (void *)mp->b_rptr; + iocp->ioc_error = 0; + cmd = iocp->ioc_cmd; + + usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + + switch (cmd) { + default: + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "unknown cmd 0x%x", cmd); + usb_release_access(ecmp->ecm_ser_acc); + miocnak(wq, mp, 0, EINVAL); + + return; + } +} + +/* + * callback functions for get/set properties + * Does not do anything. Will be enhanced to + * support set/get properties in future. + */ +/*ARGSUSED*/ +static int +usbecm_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, + uint_t wldp_length, const void *wldp_buf) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + int err = ENOTSUP; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_setprop: entry"); + + return (err); +} + +/*ARGSUSED*/ +static int usbecm_m_getprop(void *arg, const char *pr_name, + mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + int err = ENOTSUP; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_getprop: entry"); + + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { + mutex_exit(&ecmp->ecm_mutex); + + return (EIO); + } + mutex_exit(&ecmp->ecm_mutex); + + return (err); +} + +/* + * Transmit a data frame. + */ +static mblk_t * +usbecm_m_tx(void *arg, mblk_t *mp) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)arg; + mblk_t *next; + int count = 0; + + ASSERT(mp != NULL); + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_tx: entry"); + + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_tx: device not ONLINE"); + mutex_exit(&ecmp->ecm_mutex); + + return (mp); + } + mutex_exit(&ecmp->ecm_mutex); + + usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + + /* + * To make use of the device maximum capability, + * concatenate msg blocks in a msg to ETHERMAX length. + */ + while (mp != NULL) { + next = mp->b_next; + mp->b_next = NULL; + + if (usbecm_send_data(ecmp, mp) != DDI_SUCCESS) { + USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_tx: send data fail"); + + /* failure statistics */ + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_stat.es_oerrors++; + mutex_exit(&ecmp->ecm_mutex); + + mp->b_next = next; + + break; + } + + /* + * To make it simple, we count all packets, no matter + * the device supports ethernet statistics or not. + */ + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_stat.es_opackets++; + ecmp->ecm_stat.es_obytes += MBLKL(mp); + mutex_exit(&ecmp->ecm_mutex); + + freemsg(mp); /* free this msg upon success */ + + mp = next; + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_m_tx: %d msgs processed", ++count); + } + + usb_release_access(ecmp->ecm_ser_acc); + + return (mp); +} + +/* + * usbecm_bulkin_cb: + * Bulk In regular and exeception callback; + * USBA framework will call this callback + * after deal with bulkin request. + */ +/*ARGSUSED*/ +static void +usbecm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private; + mblk_t *data, *mp; + int data_len; + int max_pkt_size = ecmp->ecm_bulkin_sz; + + data = req->bulk_data; + data_len = (data) ? MBLKL(data) : 0; + + ASSERT(data->b_cont == NULL); + + mutex_enter(&ecmp->ecm_mutex); + + USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh, + "usbecm_bulkin_cb: state=%d, len=%d", ecmp->ecm_bulkin_state, + data_len); + + /* + * may receive a zero length packet according + * to USB short packet semantics + */ + if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) && + (req->bulk_completion_reason == USB_CR_OK)) { + if (data_len) { + if (ecmp->ecm_rcv_queue == NULL) { + ecmp->ecm_rcv_queue = data; + } else { + if ((msgsize(ecmp->ecm_rcv_queue) + data_len) + > ETHERMAX) { + /* + * Exceed the ethernet maximum length, we think + * something is wrong with this frame and hence + * free older data. Accept new data instead. + */ + freemsg(ecmp->ecm_rcv_queue); + ecmp->ecm_rcv_queue = data; + } else { + linkb(ecmp->ecm_rcv_queue, data); + } + } + } else { + /* + * Do not put zero length packet to receive queue. + * Otherwise, msgpullup will dupmsg() a zero length + * mblk, which will cause memleaks. + */ + freemsg(data); + } + + /* + * ECM V1.2, section 3.3.1, a short(including zero length) + * packet signifies end of frame. We can submit this frame + * to upper layer now. + */ + if ((data_len < max_pkt_size) && + (msgsize(ecmp->ecm_rcv_queue) > 0)) { + mp = msgpullup(ecmp->ecm_rcv_queue, -1); + freemsg(ecmp->ecm_rcv_queue); + ecmp->ecm_rcv_queue = NULL; + + ecmp->ecm_stat.es_ipackets++; + ecmp->ecm_stat.es_ibytes += msgsize(mp); + if (mp && (mp->b_rptr[0] & 0x01)) { + if (bcmp(mp->b_rptr, usbecm_broadcast, + ETHERADDRL) != 0) { + ecmp->ecm_stat.es_multircv++; + } else { + ecmp->ecm_stat.es_brdcstrcv++; + } + } + + if (mp) { + mutex_exit(&ecmp->ecm_mutex); + mac_rx(ecmp->ecm_mh, NULL, mp); + mutex_enter(&ecmp->ecm_mutex); + } + } + + /* prevent USBA from freeing data along with the request */ + req->bulk_data = NULL; + } else if (req->bulk_completion_reason != USB_CR_OK) { + ecmp->ecm_stat.es_ierrors++; + } + mutex_exit(&ecmp->ecm_mutex); + + usb_free_bulk_req(req); + + /* receive more */ + mutex_enter(&ecmp->ecm_mutex); + if (((ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) || + (ecmp->ecm_bulkin_state == USBECM_PIPE_IDLE)) && + (ecmp->ecm_dev_state == USB_DEV_ONLINE)) { + if (usbecm_rx_start(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, + "usbecm_bulkin_cb: restart rx fail " + "ecmp_state = %d", ecmp->ecm_bulkin_state); + } + } else if (ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) { + ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; + } + mutex_exit(&ecmp->ecm_mutex); +} + +/* + * usbsecm_rx_start: + * start data receipt + */ +static int +usbecm_rx_start(usbecm_state_t *ecmp) +{ + usb_bulk_req_t *br; + int rval = USB_FAILURE; + int data_len; + + ASSERT(mutex_owned(&ecmp->ecm_mutex)); + + DTRACE_PROBE2(usbecm_rx__start, int, ecmp->ecm_xfer_sz, + int, ecmp->ecm_bulkin_sz); + + ecmp->ecm_bulkin_state = USBECM_PIPE_BUSY; + data_len = ecmp->ecm_bulkin_sz; + + mutex_exit(&ecmp->ecm_mutex); + br = usb_alloc_bulk_req(ecmp->ecm_dip, data_len, USB_FLAGS_SLEEP); + if (br == NULL) { + USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_rx_start: allocate bulk request failed"); + + mutex_enter(&ecmp->ecm_mutex); + + return (USB_FAILURE); + } + /* initialize bulk in request. */ + br->bulk_len = data_len; + br->bulk_timeout = 0; + br->bulk_cb = usbecm_bulkin_cb; + br->bulk_exc_cb = usbecm_bulkin_cb; + br->bulk_client_private = (usb_opaque_t)ecmp; + br->bulk_attributes = USB_ATTRS_AUTOCLEARING + | USB_ATTRS_SHORT_XFER_OK; + + rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkin_ph, br, 0); + mutex_enter(&ecmp->ecm_mutex); + if (rval != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_rx_start: bulk transfer failed %d", rval); + usb_free_bulk_req(br); + ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; + } + + return (rval); +} + +/* + * usbecm_bulkout_cb: + * Bulk Out regular and exeception callback; + * USBA framework will call this callback function + * after deal with bulkout request. + */ +/*ARGSUSED*/ +static void +usbecm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private; + int data_len; + boolean_t need_update = B_FALSE; + + data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0; + + USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh, + "usbecm_bulkout_cb: data_len = %d, cr=%d", data_len, + req->bulk_completion_reason); + + mutex_enter(&ecmp->ecm_mutex); + if ((data_len > 0) && (ecmp->ecm_tx_cnt > 0)) { + if (ecmp->ecm_tx_cnt == usbecm_tx_max) { + need_update = B_TRUE; + } + ecmp->ecm_tx_cnt--; + } + mutex_exit(&ecmp->ecm_mutex); + + if (req->bulk_completion_reason && (data_len > 0)) { + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_stat.es_oerrors++; + mutex_exit(&ecmp->ecm_mutex); + + need_update = B_TRUE; + } + + /* + * notify MAC layer to retransfer the failed packet + * Or notity MAC that we have more buffer now. + */ + if (need_update) { + mac_tx_update(ecmp->ecm_mh); + } + + usb_free_bulk_req(req); +} + +static int +usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data) +{ + usb_bulk_req_t *br; + int rval = USB_FAILURE; + int data_len = MBLKL(data); + int max_pkt_size; + mblk_t *new_data = NULL; + int new_data_len = 0; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: length = %d, total len=%d", + data_len, (int)msgdsize(data)); + + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_tx_cnt >= usbecm_tx_max) { + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: (%d) exceeds TX max queue length", + ecmp->ecm_tx_cnt); + mutex_exit(&ecmp->ecm_mutex); + + return (USB_FAILURE); + } + mutex_exit(&ecmp->ecm_mutex); + + data_len = msgsize(data); + if (data_len > ETHERMAX) { + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_stat.es_macxmt_err++; + mutex_exit(&ecmp->ecm_mutex); + + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: packet too long, %d", data_len); + + return (USB_FAILURE); + } + + if (data_len < ETHERMIN) { + mblk_t *tmp; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: short packet, padding to ETHERMIN"); + + new_data_len = ETHERMIN; + if ((new_data = allocb(new_data_len, 0)) == NULL) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: fail to allocb"); + + return (USB_FAILURE); + } + bzero(new_data->b_wptr, new_data_len); + for (tmp = data; tmp != NULL; tmp = tmp->b_cont) { + bcopy(tmp->b_rptr, new_data->b_wptr, MBLKL(tmp)); + new_data->b_wptr += MBLKL(tmp); + } + + new_data->b_wptr = new_data->b_rptr + new_data_len; + } + + br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); + if (br == NULL) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: alloc req failed."); + + return (USB_FAILURE); + } + + /* initialize the bulk out request */ + if (new_data) { + br->bulk_data = msgpullup(new_data, -1); /* msg allocated! */ + br->bulk_len = new_data_len; + } else { + br->bulk_data = msgpullup(data, -1); /* msg allocated! */ + br->bulk_len = data_len; + } + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: bulk_len = %d", br->bulk_len); + + br->bulk_timeout = USBECM_BULKOUT_TIMEOUT; + br->bulk_cb = usbecm_bulkout_cb; + br->bulk_exc_cb = usbecm_bulkout_cb; + br->bulk_client_private = (usb_opaque_t)ecmp; + br->bulk_attributes = USB_ATTRS_AUTOCLEARING; + + if (br->bulk_data != NULL) { + if (br->bulk_data->b_rptr[0] & 0x01) { + mutex_enter(&ecmp->ecm_mutex); + if (bcmp(br->bulk_data->b_rptr, usbecm_broadcast, + ETHERADDRL) != 0) { + ecmp->ecm_stat.es_multixmt++; + } else { + ecmp->ecm_stat.es_brdcstxmt++; + } + mutex_exit(&ecmp->ecm_mutex); + } + rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0); + } + + if (rval != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: Send Data failed."); + + /* + * br->bulk_data should be freed because we allocated + * it in this function. + */ + usb_free_bulk_req(br); + + } else { + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_tx_cnt++; + mutex_exit(&ecmp->ecm_mutex); + + /* + * ECM V1.2, section 3.3.1, a short(including zero length) + * packet signifies end of frame. We should send a zero length + * packet to device if the total data lenght is multiple of + * bulkout endpoint's max packet size. + */ + max_pkt_size = ecmp->ecm_bulk_out_ep->ep_descr.wMaxPacketSize; + if ((data_len % max_pkt_size) == 0) { + if ((rval = usbecm_send_zero_data(ecmp)) + != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: fail to send padding"); + } + } + } + + if (new_data) { + freemsg(new_data); + } + + USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, + "usbecm_send_data: len(%d) data sent, rval=%d", + new_data_len ? new_data_len : data_len, rval); + + return (rval); +} + +static int +usbecm_send_zero_data(usbecm_state_t *ecmp) +{ + usb_bulk_req_t *br; + int rval = USB_FAILURE; + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_zero_data: entry"); + + br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); + if (br == NULL) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_data: alloc req failed."); + + return (USB_FAILURE); + } + + /* initialize the bulk out request */ + br->bulk_len = 0; + br->bulk_timeout = USBECM_BULKOUT_TIMEOUT; + br->bulk_cb = usbecm_bulkout_cb; + br->bulk_exc_cb = usbecm_bulkout_cb; + br->bulk_client_private = (usb_opaque_t)ecmp; + br->bulk_attributes = USB_ATTRS_AUTOCLEARING; + + rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0); + + if (rval != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_zero_data: Send data failed, rval=%d", + rval); + + /* + * br->bulk_data should be freed because we allocated + * it in this function. + */ + usb_free_bulk_req(br); + + } + + USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh, + "usbecm_send_zero_data: end"); + + return (rval); +} + +/* + * Loadable module configuration entry points + */ + +/* + * _init module entry point. + * + * Called when the module is being loaded into memory. + */ +int +_init(void) +{ + int err; + + err = ddi_soft_state_init(&usbecm_statep, sizeof (usbecm_state_t), 1); + + if (err != DDI_SUCCESS) + return (err); + + mac_init_ops(&usbecm_devops, "usbecm"); + err = mod_install(&usbecm_ml); + + if (err != DDI_SUCCESS) { + mac_fini_ops(&usbecm_devops); + ddi_soft_state_fini(&usbecm_statep); + } + + return (err); +} + +/* + * _info module entry point. + * + * Called to obtain information about the module. + */ +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&usbecm_ml, modinfop)); +} + +/* + * _fini module entry point. + * + * Called when the module is being unloaded. + */ +int +_fini(void) +{ + int err; + + err = mod_remove(&usbecm_ml); + if (err == DDI_SUCCESS) { + mac_fini_ops(&usbecm_devops); + ddi_soft_state_fini(&usbecm_statep); + } + + return (err); +} + +/* + * usbecm_pipe_start_polling: + * start polling on the interrupt pipe + */ +static void +usbecm_pipe_start_polling(usbecm_state_t *ecmp) +{ + usb_intr_req_t *intr; + int rval; + + USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_pipe_start_polling: "); + + if (ecmp->ecm_intr_ph == NULL) { + + return; + } + + intr = usb_alloc_intr_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP); + + /* + * If it is in interrupt context, usb_alloc_intr_req will return NULL if + * called with SLEEP flag. + */ + if (!intr) { + USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_pipe_start_polling: alloc req failed."); + + return; + } + + /* initialize the interrupt request. */ + intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK | + USB_ATTRS_AUTOCLEARING; + intr->intr_len = ecmp->ecm_intr_ep->ep_descr.wMaxPacketSize; + intr->intr_client_private = (usb_opaque_t)ecmp; + intr->intr_cb = usbecm_intr_cb; + intr->intr_exc_cb = usbecm_intr_ex_cb; + + rval = usb_pipe_intr_xfer(ecmp->ecm_intr_ph, intr, USB_FLAGS_SLEEP); + + mutex_enter(&ecmp->ecm_mutex); + if (rval == USB_SUCCESS) { + ecmp->ecm_intr_state = USBECM_PIPE_BUSY; + } else { + usb_free_intr_req(intr); + ecmp->ecm_intr_state = USBECM_PIPE_IDLE; + USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_pipe_start_polling: failed (%d)", rval); + } + mutex_exit(&ecmp->ecm_mutex); + + USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_pipe_start_polling: end, rval=%d", rval); +} + + +/* + * usbsecm_intr_cb: + * interrupt pipe normal callback + */ +/*ARGSUSED*/ +static void +usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private; + mblk_t *data = req->intr_data; + int data_len; + + data_len = (data) ? MBLKL(data) : 0; + + DTRACE_PROBE2(usbecm_intr__cb, (usb_intr_req_t *), req, int, data_len); + + /* check data length */ + if (data_len < 8) { + USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_intr_cb: %d packet too short", data_len); + usb_free_intr_req(req); + + return; + } + req->intr_data = NULL; + usb_free_intr_req(req); + + mutex_enter(&ecmp->ecm_mutex); + /* parse interrupt data -- notifications */ + usbecm_parse_intr_data(ecmp, data); + mutex_exit(&ecmp->ecm_mutex); +} + + +/* + * usbsecm_intr_ex_cb: + * interrupt pipe exception callback + */ +/*ARGSUSED*/ +static void +usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private; + usb_cr_t cr = req->intr_completion_reason; + + DTRACE_PROBE2(usbecm_intr_ex__cb, int, ecmp->ecm_dev_state, + (usb_cr_t), cr); + + usb_free_intr_req(req); + + /* + * If completion reason isn't USB_CR_PIPE_CLOSING and + * USB_CR_STOPPED_POLLING, restart polling. + */ + if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) { + mutex_enter(&ecmp->ecm_mutex); + + if (ecmp->ecm_dev_state != USB_DEV_ONLINE) { + + USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_intr_ex_cb: state = %d", + ecmp->ecm_dev_state); + + mutex_exit(&ecmp->ecm_mutex); + + return; + } + mutex_exit(&ecmp->ecm_mutex); + + usbecm_pipe_start_polling(ecmp); + } +} + + +/* + * usbsecm_parse_intr_data: + * Parse data received from interrupt callback + */ +static void +usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data) +{ + uint8_t bmRequestType; + uint8_t bNotification; + uint16_t wValue; + uint16_t wLength; + int linkstate; + + bmRequestType = data->b_rptr[0]; + bNotification = data->b_rptr[1]; + /* + * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1, + * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0, + * mLength is 2. So we directly get the value from the byte. + */ + wValue = data->b_rptr[2]; + wLength = data->b_rptr[6]; + + if (ecmp->ecm_compatibility) { + if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) { + USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_parse_intr_data: unknown request " + "type - 0x%x", bmRequestType); + + freemsg(data); + + return; + } + } else { + /* non-compatible device specific parsing */ + if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) { + if (ecmp->ecm_ds_ops->ecm_ds_intr_cb(ecmp, data) + != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_parse_intr_data: unknown request" + "type - 0x%x", bmRequestType); + } + } + freemsg(data); + + return; + } + + /* + * Check the return value of compatible devices + */ + switch (bNotification) { + case USB_CDC_NOTIFICATION_NETWORK_CONNECTION: + USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_parse_intr_data: %s network!", + wValue ? "connected to" :"disconnected from"); + + linkstate = wValue ? LINK_STATE_UP:LINK_STATE_DOWN; + if (ecmp->ecm_stat.es_linkstate == linkstate) { + /* no changes to previous state */ + break; + } + + ecmp->ecm_stat.es_linkstate = linkstate; + mutex_exit(&ecmp->ecm_mutex); + mac_link_update(ecmp->ecm_mh, linkstate); + mutex_enter(&ecmp->ecm_mutex); + + break; + case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE: + USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_parse_intr_data: A response is a available."); + + break; + case USB_CDC_NOTIFICATION_SPEED_CHANGE: + USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_parse_intr_data: speed change"); + + /* check the parameter's length. */ + if (wLength != 8) { + USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_parse_intr_data: error data length."); + } else { + uint32_t us_rate, ds_rate; + uint8_t *sp; + + sp = &data->b_rptr[8]; + LE_TO_UINT32(sp, us_rate); + sp = &data->b_rptr[12]; + LE_TO_UINT32(sp, ds_rate); + ecmp->ecm_stat.es_upspeed = us_rate; + ecmp->ecm_stat.es_downspeed = ds_rate; + } + + break; + default: + USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh, + "usbsecm_parse_intr_data: unknown notification - 0x%x!", + bNotification); + + break; + } + + freemsg(data); +} + +/* + * usbecm_restore_device_state: + * restore device state after CPR resume or reconnect + */ +static int +usbecm_restore_device_state(usbecm_state_t *ecmp) +{ + int state; + + USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, + "usbecm_restore_device_state: "); + + mutex_enter(&ecmp->ecm_mutex); + state = ecmp->ecm_dev_state; + mutex_exit(&ecmp->ecm_mutex); + + /* Check device status */ + if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) { + + return (state); + } + + /* Check if we are talking to the same device */ + if (usb_check_same_device(ecmp->ecm_dip, ecmp->ecm_lh, USB_LOG_L0, + -1, USB_CHK_ALL, NULL) != USB_SUCCESS) { + mutex_enter(&ecmp->ecm_mutex); + state = ecmp->ecm_dev_state = USB_DEV_DISCONNECTED; + mutex_exit(&ecmp->ecm_mutex); + + return (state); + } + + if (state == USB_DEV_DISCONNECTED) { + USB_DPRINTF_L1(PRINT_MASK_EVENTS, ecmp->ecm_lh, + "usbecm_restore_device_state: Device has been reconnected " + "but data may have been lost"); + } + + /* if MAC was started, restarted it */ + mutex_enter(&ecmp->ecm_mutex); + if (ecmp->ecm_mac_state == USBECM_MAC_STARTED) { + USB_DPRINTF_L3(PRINT_MASK_EVENTS, ecmp->ecm_lh, + "usbecm_restore_device_state: MAC was started"); + + mutex_exit(&ecmp->ecm_mutex); + /* Do the same operation as usbecm_m_start() does */ + if (usbecm_open_pipes(ecmp) != USB_SUCCESS) { + + return (state); + } + + mutex_enter(&ecmp->ecm_mutex); + if (usbecm_rx_start(ecmp) != USB_SUCCESS) { + mutex_exit(&ecmp->ecm_mutex); + + return (state); + } + } + mutex_exit(&ecmp->ecm_mutex); + + /* + * init device state + */ + mutex_enter(&ecmp->ecm_mutex); + state = ecmp->ecm_dev_state = USB_DEV_ONLINE; + mutex_exit(&ecmp->ecm_mutex); + + return (state); +} + +/* + * usbecm_reconnect_event_cb: + * called upon when the device is hotplugged back + */ +/*ARGSUSED*/ +static int +usbecm_reconnect_event_cb(dev_info_t *dip) +{ + usbecm_state_t *ecmp = + (usbecm_state_t *)ddi_get_soft_state(usbecm_statep, + ddi_get_instance(dip)); + + ASSERT(ecmp != NULL); + + USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, + "usbecm_reconnect_event_cb: entry"); + + (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + + mutex_enter(&ecmp->ecm_mutex); + ASSERT(ecmp->ecm_dev_state == USB_DEV_DISCONNECTED); + + mutex_exit(&ecmp->ecm_mutex); + + if (usbecm_restore_device_state(ecmp) != USB_DEV_ONLINE) { + usb_release_access(ecmp->ecm_ser_acc); + + return (USB_FAILURE); + } + + usb_release_access(ecmp->ecm_ser_acc); + + return (USB_SUCCESS); +} + + +/* + * usbecm_disconnect_event_cb: + * callback for disconnect events + */ +/*ARGSUSED*/ +static int +usbecm_disconnect_event_cb(dev_info_t *dip) +{ + usbecm_state_t *ecmp = (usbecm_state_t *)ddi_get_soft_state( + usbecm_statep, ddi_get_instance(dip)); + + USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, + "usbecm_disconnect_event_cb: entry"); + + (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_dev_state = USB_DEV_DISCONNECTED; + mutex_exit(&ecmp->ecm_mutex); + + usbecm_close_pipes(ecmp); + + usb_release_access(ecmp->ecm_ser_acc); + + USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh, + "usbecm_disconnect_event_cb: End"); + + return (USB_SUCCESS); +} + +/* + * power management + * ---------------- + * + * usbecm_create_pm_components: + * create PM components + */ +static int +usbecm_create_pm_components(usbecm_state_t *ecmp) +{ + dev_info_t *dip = ecmp->ecm_dip; + usbecm_pm_t *pm; + uint_t pwr_states; + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_create_pm_components: entry"); + + if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_create_pm_components: failed"); + + /* don't fail the attach process */ + return (USB_SUCCESS); + } + + pm = ecmp->ecm_pm = + (usbecm_pm_t *)kmem_zalloc(sizeof (usbecm_pm_t), KM_SLEEP); + + pm->pm_pwr_states = (uint8_t)pwr_states; + pm->pm_cur_power = USB_DEV_OS_FULL_PWR; + pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip, + USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS); + + (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); + + return (USB_SUCCESS); +} + +/* + * usbecm_cleanup: + * Release resources of current device during detach. + */ +static void +usbecm_cleanup(usbecm_state_t *ecmp) +{ + USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, + "usbecm_cleanup: "); + + if (ecmp == NULL) { + + return; + } + + usbecm_close_pipes(ecmp); + + /* unregister callback function */ + if (ecmp->ecm_init_flags & USBECM_INIT_EVENTS) { + USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, + "usbecm_cleanup: unregister events"); + + usb_unregister_event_cbs(ecmp->ecm_dip, &usbecm_events); + } + + /* destroy power management components */ + if (ecmp->ecm_pm != NULL) { + USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, + "usbecm_cleanup: destroy pm"); + usbecm_destroy_pm_components(ecmp); + } + + /* free description of device tree. */ + if (ecmp->ecm_def_ph != NULL) { + mutex_destroy(&ecmp->ecm_mutex); + + usb_free_descr_tree(ecmp->ecm_dip, ecmp->ecm_dev_data); + ecmp->ecm_def_ph = NULL; + } + + if (ecmp->ecm_lh != NULL) { + usb_free_log_hdl(ecmp->ecm_lh); + ecmp->ecm_lh = NULL; + } + + /* detach client device */ + if (ecmp->ecm_dev_data != NULL) { + usb_client_detach(ecmp->ecm_dip, ecmp->ecm_dev_data); + } + + if (ecmp->ecm_init_flags & USBECM_INIT_MAC) { + usbecm_mac_fini(ecmp); + } + + if (ecmp->ecm_init_flags & USBECM_INIT_SER) { + usb_fini_serialization(ecmp->ecm_ser_acc); + } + + ddi_prop_remove_all(ecmp->ecm_dip); + ddi_remove_minor_node(ecmp->ecm_dip, NULL); +} + +/* + * usbecm_destroy_pm_components: + * destroy PM components + */ +static void +usbecm_destroy_pm_components(usbecm_state_t *ecmp) +{ + usbecm_pm_t *pm = ecmp->ecm_pm; + dev_info_t *dip = ecmp->ecm_dip; + int rval; + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_destroy_pm_components: "); + + if (ecmp->ecm_dev_state != USB_DEV_DISCONNECTED) { + if (pm->pm_wakeup_enabled) { + rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); + if (rval != DDI_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_destroy_pm_components: " + "raising power failed (%d)", rval); + } + + rval = usb_handle_remote_wakeup(dip, + USB_REMOTE_WAKEUP_DISABLE); + if (rval != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_destroy_pm_components: " + "disable remote wakeup failed (%d)", rval); + } + } + + (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF); + } + kmem_free((caddr_t)pm, sizeof (usbecm_pm_t)); + ecmp->ecm_pm = NULL; +} + +/* + * usbecm_pm_set_busy: + * mark device busy and raise power + */ +static void +usbecm_pm_set_busy(usbecm_state_t *ecmp) +{ + usbecm_pm_t *pm = ecmp->ecm_pm; + dev_info_t *dip = ecmp->ecm_dip; + int rval; + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_pm_set_busy: pm = 0x%p", (void *)pm); + + if (pm == NULL) { + + return; + } + + mutex_enter(&ecmp->ecm_mutex); + /* if already marked busy, just increment the counter */ + if (pm->pm_busy_cnt++ > 0) { + mutex_exit(&ecmp->ecm_mutex); + + return; + } + + (void) pm_busy_component(dip, 0); + + if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) { + mutex_exit(&ecmp->ecm_mutex); + + return; + } + + /* need to raise power */ + pm->pm_raise_power = B_TRUE; + mutex_exit(&ecmp->ecm_mutex); + + rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); + if (rval != DDI_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_pm_set_busy: raising power failed"); + } + + mutex_enter(&ecmp->ecm_mutex); + pm->pm_raise_power = B_FALSE; + mutex_exit(&ecmp->ecm_mutex); +} + + +/* + * usbecm_pm_set_idle: + * mark device idle + */ +static void +usbecm_pm_set_idle(usbecm_state_t *ecmp) +{ + usbecm_pm_t *pm = ecmp->ecm_pm; + dev_info_t *dip = ecmp->ecm_dip; + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_pm_set_idle: "); + + if (pm == NULL) { + + return; + } + + mutex_enter(&ecmp->ecm_mutex); + if (--pm->pm_busy_cnt > 0) { + mutex_exit(&ecmp->ecm_mutex); + + return; + } + + if (pm) { + (void) pm_idle_component(dip, 0); + } + mutex_exit(&ecmp->ecm_mutex); +} + + +/* + * usbecm_pwrlvl0: + * Functions to handle power transition for OS levels 0 -> 3 + * The same level as OS state, different from USB state + */ +static int +usbecm_pwrlvl0(usbecm_state_t *ecmp) +{ + int rval; + + ASSERT(mutex_owned(&ecmp->ecm_mutex)); + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_pwrlvl0: "); + + switch (ecmp->ecm_dev_state) { + case USB_DEV_ONLINE: + /* issue USB D3 command to the device */ + rval = usb_set_device_pwrlvl3(ecmp->ecm_dip); + ASSERT(rval == USB_SUCCESS); + if ((ecmp->ecm_intr_ph != NULL) && + (ecmp->ecm_intr_state == USBECM_PIPE_BUSY)) { + mutex_exit(&ecmp->ecm_mutex); + usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph, + USB_FLAGS_SLEEP); + mutex_enter(&ecmp->ecm_mutex); + + ecmp->ecm_intr_state = USBECM_PIPE_IDLE; + } + ecmp->ecm_dev_state = USB_DEV_PWRED_DOWN; + ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF; + + /* FALLTHRU */ + case USB_DEV_DISCONNECTED: + case USB_DEV_SUSPENDED: + /* allow a disconnect/cpr'ed device to go to lower power */ + + return (USB_SUCCESS); + case USB_DEV_PWRED_DOWN: + default: + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_pwrlvl0: illegal device state"); + + return (USB_FAILURE); + } +} + + +/* + * usbecm_pwrlvl1: + * Functions to handle power transition for OS levels 1 -> 2 + */ +static int +usbecm_pwrlvl1(usbecm_state_t *ecmp) +{ + /* issue USB D2 command to the device */ + (void) usb_set_device_pwrlvl2(ecmp->ecm_dip); + + return (USB_FAILURE); +} + + +/* + * usbecm_pwrlvl2: + * Functions to handle power transition for OS levels 2 -> 1 + */ +static int +usbecm_pwrlvl2(usbecm_state_t *ecmp) +{ + /* issue USB D1 command to the device */ + (void) usb_set_device_pwrlvl1(ecmp->ecm_dip); + + return (USB_FAILURE); +} + + +/* + * usbecm_pwrlvl3: + * Functions to handle power transition for OS levels 3 -> 0 + * The same level as OS state, different from USB state + */ +static int +usbecm_pwrlvl3(usbecm_state_t *ecmp) +{ + int rval; + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_pwrlvl3: "); + + ASSERT(mutex_owned(&ecmp->ecm_mutex)); + + switch (ecmp->ecm_dev_state) { + case USB_DEV_PWRED_DOWN: + /* Issue USB D0 command to the device here */ + rval = usb_set_device_pwrlvl0(ecmp->ecm_dip); + ASSERT(rval == USB_SUCCESS); + + if (ecmp->ecm_intr_ph != NULL && + ecmp->ecm_intr_state == USBECM_PIPE_IDLE) { + mutex_exit(&ecmp->ecm_mutex); + usbecm_pipe_start_polling(ecmp); + mutex_enter(&ecmp->ecm_mutex); + } + + ecmp->ecm_dev_state = USB_DEV_ONLINE; + ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR; + + /* FALLTHRU */ + case USB_DEV_ONLINE: + /* we are already in full power */ + + /* FALLTHRU */ + case USB_DEV_DISCONNECTED: + case USB_DEV_SUSPENDED: + + return (USB_SUCCESS); + default: + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_pwrlvl3: illegal device state"); + + return (USB_FAILURE); + } +} + +/*ARGSUSED*/ +static int +usbecm_power(dev_info_t *dip, int comp, int level) +{ + usbecm_state_t *ecmp; + usbecm_pm_t *pm; + int rval = USB_SUCCESS; + + ecmp = ddi_get_soft_state(usbecm_statep, ddi_get_instance(dip)); + pm = ecmp->ecm_pm; + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_power: entry"); + + /* check if pm is NULL */ + if (pm == NULL) { + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_power: pm is NULL."); + + return (USB_FAILURE); + } + + mutex_enter(&ecmp->ecm_mutex); + /* + * check if we are transitioning to a legal power level + */ + if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) { + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_power: " + "illegal power level %d, pwr_states=%x", + level, pm->pm_pwr_states); + mutex_exit(&ecmp->ecm_mutex); + + return (USB_FAILURE); + } + + /* + * if we are about to raise power and asked to lower power, fail + */ + if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) { + USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_power: wrong condition."); + mutex_exit(&ecmp->ecm_mutex); + + return (USB_FAILURE); + } + + /* + * Set the power status of device by request level. + */ + switch (level) { + case USB_DEV_OS_PWR_OFF: + rval = usbecm_pwrlvl0(ecmp); + + break; + case USB_DEV_OS_PWR_1: + rval = usbecm_pwrlvl1(ecmp); + + break; + case USB_DEV_OS_PWR_2: + rval = usbecm_pwrlvl2(ecmp); + + break; + case USB_DEV_OS_FULL_PWR: + rval = usbecm_pwrlvl3(ecmp); + + break; + } + + mutex_exit(&ecmp->ecm_mutex); + + return (rval); +} + +/* + * Register with the MAC layer. + */ +static int +usbecm_mac_init(usbecm_state_t *ecmp) +{ + mac_register_t *macp; + int err; + + /* + * Initialize mac structure + */ + macp = mac_alloc(MAC_VERSION); + if (macp == NULL) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "failed to allocate MAC structure"); + + return (USB_FAILURE); + } + + /* + * Initialize pointer to device specific functions + */ + macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; + macp->m_driver = ecmp; + macp->m_dip = ecmp->ecm_dip; + + macp->m_src_addr = ecmp->ecm_srcaddr; + macp->m_callbacks = &usbecm_m_callbacks; + macp->m_min_sdu = 0; + macp->m_max_sdu = ETHERMTU; + + /* + * Register the macp to mac + */ + err = mac_register(macp, &ecmp->ecm_mh); + mac_free(macp); + + if (err != DDI_SUCCESS) { + USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh, + "failed to register MAC structure"); + + return (USB_FAILURE); + } + + mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN); + ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN; + ecmp->ecm_tx_cnt = 0; + + return (USB_SUCCESS); +} + +static int +usbecm_mac_fini(usbecm_state_t *ecmp) +{ + int rval = DDI_SUCCESS; + + if ((ecmp->ecm_init_flags & USBECM_INIT_MAC) == 0) { + return (DDI_SUCCESS); + } + + ecmp->ecm_init_flags &= ~USBECM_INIT_MAC; + if ((rval = mac_disable(ecmp->ecm_mh)) != 0) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "failed to disable MAC"); + + return (rval); + } + + (void) mac_unregister(ecmp->ecm_mh); + + return (rval); +} + +static int +usbecm_resume(usbecm_state_t *ecmp) +{ + int current_state; + int ret; + + USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh, + "usbecm_resume: "); + + mutex_enter(&ecmp->ecm_mutex); + current_state = ecmp->ecm_dev_state; + mutex_exit(&ecmp->ecm_mutex); + + /* restore the status of device */ + if (current_state != USB_DEV_ONLINE) { + ret = usbecm_restore_device_state(ecmp); + } else { + ret = USB_DEV_ONLINE; + } + + return (ret); +} + +static int +usbecm_suspend(usbecm_state_t *ecmp) +{ + (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0); + + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_dev_state = USB_DEV_SUSPENDED; + mutex_exit(&ecmp->ecm_mutex); + + usbecm_close_pipes(ecmp); + + usb_release_access(ecmp->ecm_ser_acc); + + return (0); +} + +/* + * Translate MAC address from string to 6 bytes array int value + * Can't use ether_aton() since it requires format of x:x:x:x:x:x + */ +void +label_to_mac(char *hex, unsigned char *mac) +{ + int i; + char c; + + /* can only count 6 bytes! */ + for (i = 0; i < 6; i++) { + /* upper 4 bits */ + if (!isdigit(hex[2*i])) { + c = (toupper(hex[2 * i]) - 'A' + 10); + } else { + c = (hex[2 * i] - '0'); + } + mac[i] = c * 16; + + /* lower 4 bits */ + if (!isdigit(hex[2*i + 1])) { + c = (toupper(hex[2 * i + 1]) - 'A' + 10); + } else { + c = hex[2 * i + 1] - '0'; + } + mac[i] += c; + } +} + +/* + * usbecm_get_descriptors: + * parse functional descriptors of ecm compatible device + */ +static int +usbecm_get_descriptors(usbecm_state_t *ecmp) +{ + int i; + usb_cfg_data_t *cfg; + usb_alt_if_data_t *altif; + usb_cvs_data_t *cvs; + int16_t master_if = -1, slave_if = -1; + usb_cdc_ecm_descr_t ecm_desc; + usb_ep_data_t *ep_data; + usb_dev_descr_t *usb_dev_desc; + + USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: "); + + usb_dev_desc = ecmp->ecm_dev_data->dev_descr; + + /* + * Special treatment of Sun's SP Ethernet device. + */ + if ((usb_dev_desc->idVendor == SUN_SP_VENDOR_ID) && + (usb_dev_desc->idProduct == SUN_SP_PRODUCT_ID)) { + if (usb_set_cfg(ecmp->ecm_dip, ecmp->ecm_cfg_index, + USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: fail to set cfg "); + } else { + usb_free_dev_data(ecmp->ecm_dip, ecmp->ecm_dev_data); + if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data, + USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: fail to get" + " dev_data"); + + return (USB_FAILURE); + } + } + } + + cfg = ecmp->ecm_dev_data->dev_curr_cfg; + + /* set default control and data interface */ + ecmp->ecm_ctrl_if_no = ecmp->ecm_data_if_no = 0; + + /* get current interfaces */ + ecmp->ecm_ctrl_if_no = ecmp->ecm_dev_data->dev_curr_if; + if (cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt == 0) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: elements in if_alt is %d", + cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt); + + return (USB_FAILURE); + } + + altif = &cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_alt[0]; + + /* + * Based on CDC specification, ECM devices usually include the + * following function descriptors: Header, Union and ECM + * Contry Selection function descriptors. This loop search tree data + * structure for each ecm class descriptor. + */ + for (i = 0; i < altif->altif_n_cvs; i++) { + cvs = &altif->altif_cvs[i]; + + if ((cvs->cvs_buf == NULL) || + (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) { + continue; + } + + switch (cvs->cvs_buf[2]) { + case USB_CDC_DESCR_TYPE_HEADER: + /* + * parse header functional descriptor + * Just to check integrity. + */ + if (cvs->cvs_buf_len != 5) { + return (USB_FAILURE); + } + break; + case USB_CDC_DESCR_TYPE_ETHERNET: + /* parse ECM functional descriptor */ + if (cvs->cvs_buf_len >= USB_CDC_ECM_LEN) { + char buf[USB_MAXSTRINGLEN]; + + if (usb_parse_data("4cl2sc", cvs->cvs_buf, + cvs->cvs_buf_len, (void *)&ecm_desc, + (size_t)USB_CDC_ECM_LEN) < + USB_CDC_ECM_LEN) { + + return (USB_FAILURE); + } + + /* get the MAC address */ + if (usb_get_string_descr(ecmp->ecm_dip, + USB_LANG_ID, ecm_desc.iMACAddress, buf, + USB_MAXSTRINGLEN) != USB_SUCCESS) { + + return (USB_FAILURE); + } + + USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: macaddr=%s ", + buf); + + /* expects 12 characters */ + if (strlen(buf) < 12) { + return (USB_FAILURE); + } + label_to_mac(buf, ecmp->ecm_srcaddr); + + bcopy(&ecm_desc, &ecmp->ecm_desc, + USB_CDC_ECM_LEN); + } + break; + case USB_CDC_DESCR_TYPE_UNION: + /* parse Union functional descriptor. */ + if (cvs->cvs_buf_len >= 5) { + master_if = cvs->cvs_buf[3]; + slave_if = cvs->cvs_buf[4]; + } + break; + default: + break; + } + } + + /* For usb ecm devices, it must satisfy the following options. */ + if (cfg->cfg_n_if < 2) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: # of interfaces %d < 2", + cfg->cfg_n_if); + + return (USB_FAILURE); + } + + if (ecmp->ecm_data_if_no == 0 && + slave_if != ecmp->ecm_data_if_no) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: Device has no call management " + "descriptor and use Union Descriptor."); + + ecmp->ecm_data_if_no = slave_if; + } + + if ((master_if != ecmp->ecm_ctrl_if_no) || + (slave_if != ecmp->ecm_data_if_no)) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: control interface or " + "data interface don't match."); + + return (USB_FAILURE); + } + + if ((ecmp->ecm_ctrl_if_no >= cfg->cfg_n_if) || + (ecmp->ecm_data_if_no >= cfg->cfg_n_if)) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: control interface %d or " + "data interface %d out of range.", + ecmp->ecm_ctrl_if_no, ecmp->ecm_data_if_no); + + return (USB_FAILURE); + } + + /* ECM data interface has a minimal of two altsettings */ + if (cfg->cfg_if[ecmp->ecm_data_if_no].if_n_alt < 2) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: elements in if_alt is %d," + " MUST >= 2", cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt); + + return (USB_FAILURE); + } + + /* control interface must have interrupt endpoint */ + if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, + ecmp->ecm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR, + USB_EP_DIR_IN)) == NULL) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: " + "ctrl interface %d has no interrupt endpoint", + ecmp->ecm_data_if_no); + + return (USB_FAILURE); + } + ecmp->ecm_intr_ep = ep_data; + + /* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */ + if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, + ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK, + USB_EP_DIR_IN)) == NULL) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: " + "data interface %d has no bulk in endpoint", + ecmp->ecm_data_if_no); + + return (USB_FAILURE); + } + ecmp->ecm_bulk_in_ep = ep_data; + + if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data, + ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK, + USB_EP_DIR_OUT)) == NULL) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_get_descriptors: " + "data interface %d has no bulk out endpoint", + ecmp->ecm_data_if_no); + + return (USB_FAILURE); + } + ecmp->ecm_bulk_out_ep = ep_data; + + /* set default value for ethernet packet filter */ + ecmp->ecm_pkt_flt = CDC_ECM_PKT_TYPE_DIRECTED; + + return (USB_SUCCESS); +} + +/* Generate IEEE802 style MAC address */ +static void +generate_ether_addr(uint8_t *mac_addr) +{ + random_get_bytes(mac_addr, 6); + mac_addr [0] &= 0xfe; /* unicast only */ + mac_addr [0] |= 0x02; /* set locally administered bit */ +} + +/* + * Find a pair of bulk In/Out endpoints + */ +int usbecm_find_bulk_in_out_eps(usbecm_state_t *ecmp, + uint16_t ifc, usb_if_data_t *intf) +{ + uint16_t alt, alt_num; + usb_ep_data_t *intr_ep = NULL; + usb_ep_data_t *bulk_in, *bulk_out, *ep; + + alt_num = intf->if_n_alt; + + /* + * for the non-compatible devices, to make it simple, we + * suppose the devices have this kind of configuration: + * INTR In EP(if exists) + BULK In + Bulk Out in the + * same altsetting of the same interface + */ + for (alt = 0; alt < alt_num; alt++) { + /* search pair of bulk in/out EPs */ + if (((bulk_in = usb_lookup_ep_data(ecmp->ecm_dip, + ecmp->ecm_dev_data, ifc, alt, 0, + USB_EP_ATTR_BULK, + USB_EP_DIR_IN)) == NULL) || + (bulk_out = usb_lookup_ep_data(ecmp->ecm_dip, + ecmp->ecm_dev_data, ifc, alt, 0, + USB_EP_ATTR_BULK, + USB_EP_DIR_OUT)) == NULL) { + + continue; + } + + /* + * search interrupt pipe. + */ + if ((ep = usb_lookup_ep_data(ecmp->ecm_dip, + ecmp->ecm_dev_data, ifc, alt, 0, + USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) { + intr_ep = ep; + } + + + ecmp->ecm_data_if_no = ifc; + ecmp->ecm_data_if_alt = alt; + ecmp->ecm_intr_ep = intr_ep; + ecmp->ecm_ctrl_if_no = ifc; + ecmp->ecm_bulk_in_ep = bulk_in; + ecmp->ecm_bulk_out_ep = bulk_out; + + return (USB_SUCCESS); + } + + return (USB_FAILURE); +} + +static int +usbecm_init_non_compatible_device(usbecm_state_t *ecmp) +{ + usb_if_data_t *cur_if; + uint16_t if_num, i; + + /* + * If device don't conform to spec, search pairs of bulk in/out + * endpoints and fill related structure. We suppose this driver + * is bound to a interface. + */ + cur_if = ecmp->ecm_dev_data->dev_curr_cfg->cfg_if; + if_num = ecmp->ecm_dev_data->dev_curr_cfg->cfg_n_if; + + /* search each interface which have bulk in and out */ + for (i = 0; i < if_num; i++) { + if (usbecm_find_bulk_in_out_eps(ecmp, i, + cur_if) == USB_SUCCESS) { + + break; + } + cur_if++; + } + + USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_init_non_compatible_device: ctrl_if=%d," + " data_if=%d, alt=%d", ecmp->ecm_ctrl_if_no, + ecmp->ecm_data_if_no, ecmp->ecm_data_if_alt); + + return (USB_SUCCESS); +} + +static boolean_t +usbecm_is_compatible(usbecm_state_t *ecmp) +{ + usb_cfg_data_t *cfg_data; + usb_if_data_t *intf; + usb_alt_if_data_t *alt; + int alt_num, if_num, cfg_num; + int i, j, cfg_index; + + cfg_num = ecmp->ecm_dev_data->dev_n_cfg; + USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_is_compatible: entry, cfg_num=%d", cfg_num); + + for (cfg_index = 0; cfg_index < cfg_num; cfg_index++) { + cfg_data = &(ecmp->ecm_dev_data->dev_cfg[cfg_index]); + + USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_is_compatible: cfg_index=%d, value=%d", + cfg_index, cfg_data->cfg_descr.bConfigurationValue); + + intf = cfg_data->cfg_if; + if_num = cfg_data->cfg_n_if; + + for (i = 0; i < if_num; i++) { + alt_num = intf->if_n_alt; + for (j = 0; j < alt_num; j++) { + alt = &intf->if_alt[j]; + if ((alt->altif_descr.bInterfaceClass == 0x02) && + (alt->altif_descr.bInterfaceSubClass == 0x06)) { + ecmp->ecm_cfg_index = cfg_index; + + USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_is_compatible: cfg_index=%d", + cfg_index); + + return (B_TRUE); + } + } + intf++; + } + } + + return (B_FALSE); +} + + +static int +usbecm_usb_init(usbecm_state_t *ecmp) +{ + + if (usb_client_attach(ecmp->ecm_dip, USBDRV_VERSION, 0) != + USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_usb_init: fail to attach"); + + return (USB_FAILURE); + } + + /* Get the configuration information of device */ + if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data, + USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_usb_init: fail to get_dev_data"); + + return (USB_FAILURE); + } + ecmp->ecm_def_ph = ecmp->ecm_dev_data->dev_default_ph; + ecmp->ecm_dev_state = USB_DEV_ONLINE; + + mutex_init(&ecmp->ecm_mutex, NULL, MUTEX_DRIVER, + ecmp->ecm_dev_data->dev_iblock_cookie); + + if ((strcmp(ddi_binding_name(ecmp->ecm_dip), + "usbif,class2.6") == 0) || + ((strcmp(ddi_binding_name(ecmp->ecm_dip), + "usb,class2.6.0") == 0))) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_usb_init: A CDC ECM device is attached"); + ecmp->ecm_compatibility = B_TRUE; + } else if (usb_owns_device(ecmp->ecm_dip) && + usbecm_is_compatible(ecmp)) { + /* + * Current Sun SP ECM device has two configurations. Hence + * USBA doesn't create interface level compatible names + * for it, see usba_ready_device_node(). We have to check + * manually to see if compatible interfaces exist, when + * the driver owns the entire device. + */ + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_usb_init: A CDC ECM device is attached"); + ecmp->ecm_compatibility = B_TRUE; + } else { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_usb_init: A nonstandard device is attached to " + "usbecm(7D) driver. This device doesn't conform to " + "usb cdc spec."); + ecmp->ecm_compatibility = B_FALSE; + + /* generate a random MAC addr */ + generate_ether_addr(ecmp->ecm_srcaddr); + } + + if ((ecmp->ecm_compatibility == B_TRUE) && + (usbecm_get_descriptors(ecmp) != USB_SUCCESS)) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_usb_init: A compatible device is attached, but " + "fail to get standard descriptors"); + + return (USB_FAILURE); + } + + if (ecmp->ecm_compatibility == B_FALSE) { + (void) usbecm_init_non_compatible_device(ecmp); + } + + /* Create power management components */ + if (usbecm_create_pm_components(ecmp) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_usb_init: create pm components failed."); + + return (USB_FAILURE); + } + + /* Register to get callbacks for USB events */ + if (usb_register_event_cbs(ecmp->ecm_dip, &usbecm_events, 0) + != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbsecm_attach: register event callback failed."); + + return (USB_FAILURE); + } + ecmp->ecm_init_flags |= USBECM_INIT_EVENTS; + + + /* Get max data size of bulk transfer */ + if (usb_pipe_get_max_bulk_transfer_size(ecmp->ecm_dip, + &ecmp->ecm_xfer_sz) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbsecm_ds_attach: get max size of transfer failed."); + + return (USB_FAILURE); + } + + + ecmp->ecm_ser_acc = usb_init_serialization(ecmp->ecm_dip, + USB_INIT_SER_CHECK_SAME_THREAD); + ecmp->ecm_init_flags |= USBECM_INIT_SER; + + return (USB_SUCCESS); +} + + +/* + * Open operation pipes. Each ECM device should have Bulk In, Bulk Out + * and Interrupt In endpoints + */ +static int +usbecm_open_pipes(usbecm_state_t *ecmp) +{ + int rval = USB_SUCCESS; + usb_ep_data_t *in_data, *out_data, *intr_pipe; + usb_pipe_policy_t policy; + int altif; + + ASSERT(!mutex_owned(&ecmp->ecm_mutex)); + + USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp); + + if (ecmp->ecm_compatibility == B_TRUE) { + /* compatible device has minimum of 2 altsetting, select alt 1 */ + altif = 1; + } else { + altif = ecmp->ecm_data_if_alt; + } + intr_pipe = ecmp->ecm_intr_ep; + in_data = ecmp->ecm_bulk_in_ep; + out_data = ecmp->ecm_bulk_out_ep; + + /* Bulk in and out must exist simultaneously. */ + if ((in_data == NULL) || (out_data == NULL)) { + USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbsecm_open_pipes: look up bulk pipe failed in " + "interface %d ", + ecmp->ecm_data_if_no); + + return (USB_FAILURE); + } + /* + * If device conform to ecm spec, it must have an interrupt pipe + * for this device. + */ + if (ecmp->ecm_compatibility == B_TRUE && intr_pipe == NULL) { + USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_open_pipes: look up interrupt pipe failed in " + "interface %d", ecmp->ecm_ctrl_if_no); + + return (USB_FAILURE); + } + + USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x", + intr_pipe?intr_pipe->ep_descr.bEndpointAddress:0, + in_data->ep_descr.bEndpointAddress, + out_data->ep_descr.bEndpointAddress); + + USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbsecm_open_pipes: set data if(%d) alt(%d) ", + ecmp->ecm_data_if_no, altif); + + if ((rval = usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no, + altif, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_open_pipes: set alternate failed (%d)", + rval); + + return (rval); + } + + policy.pp_max_async_reqs = 2; + + /* Open bulk in endpoint */ + if (usb_pipe_open(ecmp->ecm_dip, &in_data->ep_descr, &policy, + USB_FLAGS_SLEEP, &ecmp->ecm_bulkin_ph) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_open_pipes: open bulkin pipe failed!"); + + return (USB_FAILURE); + } + + /* Open bulk out endpoint */ + if (usb_pipe_open(ecmp->ecm_dip, &out_data->ep_descr, &policy, + USB_FLAGS_SLEEP, &ecmp->ecm_bulkout_ph) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_open_pipes: open bulkout pipe failed!"); + + usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, + USB_FLAGS_SLEEP, NULL, NULL); + + return (USB_FAILURE); + } + + /* Open interrupt endpoint if found. */ + if (intr_pipe != NULL) { + if (usb_pipe_open(ecmp->ecm_dip, &intr_pipe->ep_descr, &policy, + USB_FLAGS_SLEEP, &ecmp->ecm_intr_ph) != USB_SUCCESS) { + USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbecm_open_pipes: " + "open intr pipe failed"); + + usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, + USB_FLAGS_SLEEP, NULL, NULL); + usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph, + USB_FLAGS_SLEEP, NULL, NULL); + + return (USB_FAILURE); + } + } + + /* initialize the pipe related data */ + mutex_enter(&ecmp->ecm_mutex); + ecmp->ecm_bulkin_sz = in_data->ep_descr.wMaxPacketSize; + ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE; + ecmp->ecm_bulkout_state = USBECM_PIPE_IDLE; + if (ecmp->ecm_intr_ph != NULL) { + ecmp->ecm_intr_state = USBECM_PIPE_IDLE; + } + mutex_exit(&ecmp->ecm_mutex); + + if (ecmp->ecm_intr_ph != NULL) { + + usbecm_pipe_start_polling(ecmp); + } + + USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh, + "usbsecm_open_pipes: end"); + + return (rval); +} + + +/* + * usbsecm_close_pipes: + * Close pipes + * Each device could include three pipes: bulk in, bulk out and interrupt. + */ +static void +usbecm_close_pipes(usbecm_state_t *ecmp) +{ + + mutex_enter(&ecmp->ecm_mutex); + + USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, + "usbsecm_close_pipes: ecm_bulkin_state = %d", + ecmp->ecm_bulkin_state); + + /* + * Check the status of the pipes. If pipe is closing or closed, + * return directly. + */ + if ((ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSED) || + (ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSING)) { + USB_DPRINTF_L2(PRINT_MASK_CLOSE, ecmp->ecm_lh, + "usbsecm_close_pipes: pipe is closing or has closed"); + mutex_exit(&ecmp->ecm_mutex); + + return; + } + + ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSING; + mutex_exit(&ecmp->ecm_mutex); + + /* reset the data interface's altsetting to 0 */ + if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) && + (usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no, + 0, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS)) { + USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh, + "usbecm_close_pipes: reset alternate failed "); + } + + /* Close pipes */ + usb_pipe_reset(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, + USB_FLAGS_SLEEP, NULL, 0); + usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph, + USB_FLAGS_SLEEP, NULL, 0); + usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph, + USB_FLAGS_SLEEP, NULL, 0); + + if (ecmp->ecm_intr_ph != NULL) { + usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph, + USB_FLAGS_SLEEP); + usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_intr_ph, + USB_FLAGS_SLEEP, NULL, 0); + } + + mutex_enter(&ecmp->ecm_mutex); + /* Reset the status of pipes to closed */ + ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSED; + ecmp->ecm_bulkin_ph = NULL; + ecmp->ecm_bulkout_state = USBECM_PIPE_CLOSED; + ecmp->ecm_bulkout_ph = NULL; + if (ecmp->ecm_intr_ph != NULL) { + ecmp->ecm_intr_state = USBECM_PIPE_CLOSED; + ecmp->ecm_intr_ph = NULL; + } + + mutex_exit(&ecmp->ecm_mutex); + + USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh, + "usbsecm_close_pipes: pipes have been closed."); +} + + +static int +usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request, + uint16_t value, mblk_t **data) +{ + usb_ctrl_setup_t setup; + usb_cb_flags_t cb_flags; + usb_cr_t cr; + int rval; + + USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, + "usbecm_ctrl_write: "); + + /* initialize the control request. */ + setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV | + USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; + setup.bRequest = request; + setup.wValue = value; + setup.wIndex = ecmp->ecm_ctrl_if_no; + setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0; + setup.attrs = 0; + + rval = usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data, + &cr, &cb_flags, 0); + + USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, + "usbecm_ctrl_write: rval = %d", rval); + + return (rval); +} + +static int +usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request, + uint16_t value, mblk_t **data, int len) +{ + usb_ctrl_setup_t setup; + usb_cb_flags_t cb_flags; + usb_cr_t cr; + + USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh, + "usbecm_ctrl_read: "); + + /* initialize the control request. */ + setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST | + USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF; + setup.bRequest = request; + setup.wValue = value; + setup.wIndex = ecmp->ecm_ctrl_if_no; + setup.wLength = (uint16_t)len; + setup.attrs = 0; + + return (usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data, + &cr, &cb_flags, 0)); +} + +/* Get specific statistic data from device */ +static int +usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, uint32_t *stat_data) +{ + mblk_t *data = NULL; + uint32_t stat; + + /* first check to see if this stat is collected by device */ + if ((ecmp->ecm_compatibility == B_TRUE) && + (ecmp->ecm_desc.bmEthernetStatistics & ECM_STAT_CAP_MASK(fs))) { + if (usbecm_ctrl_read(ecmp, CDC_ECM_GET_ETH_STAT, + ecmp->ecm_ctrl_if_no, &data, 4) != USB_SUCCESS) { + + return (USB_FAILURE); + } + stat = (data->b_rptr[3] << 24) | (data->b_rptr[2] << 16) | + (data->b_rptr[1] << 8) | (data->b_rptr[0]); + *stat_data = stat; + + freemsg(data); + + return (USB_SUCCESS); + } + + return (USB_FAILURE); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/warlock/usbecm.wlcmd Fri Mar 12 09:19:50 2010 +0800 @@ -0,0 +1,72 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +one usbecm_state + +### specify the root functions + +root usbecm_bulkin_cb +root usbecm_bulkout_cb +root usbecm_intr_cb +root usbecm_intr_ex_cb + +### specify usbecm function pointers + + +root usbecm_bulkin_cb +root usbecm_bulkout_cb +root usbecm_intr_cb +root usbecm_intr_ex_cb + +# temporary +root usbecm_pm_set_busy +root usbecm_pm_set_idle +root usbecm_power + +root usbecm_m_stop +root usbecm_m_start +root usbecm_m_unicst +root usbecm_m_multicst +root usbecm_m_promisc +root usbecm_m_ioctl +root usbecm_m_tx +root usbecm_m_getprop +root usbecm_m_setprop +root usbecm_m_stat + +root usbecm_disconnect_event_cb +root usbecm_reconnect_event_cb + +add bus_ops::bus_add_eventcall targets warlock_dummy +add bus_ops::bus_get_eventcookie targets warlock_dummy +add bus_ops::bus_post_event targets warlock_dummy +add bus_ops::bus_remove_eventcall targets warlock_dummy +add bus_ops::bus_intr_ctl targets warlock_dummy +add bus_ops::bus_config targets warlock_dummy +add bus_ops::bus_unconfig targets warlock_dummy + +add usbecm_ds_ops::ecm_ds_init targets warlock_dummy +add usbecm_ds_ops::ecm_ds_fini targets warlock_dummy +add usbecm_ds_ops::ecm_ds_start targets warlock_dummy +add usbecm_ds_ops::ecm_ds_stop targets warlock_dummy +add usbecm_ds_ops::ecm_ds_intr_cb targets warlock_dummy
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/warlock/usbecm_with_usba.wlcmd Fri Mar 12 09:19:50 2010 +0800 @@ -0,0 +1,187 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +one ohci_state +one ehci_state +one uhci_state +one usbecm_state + +### specify the root functions + +root usb_console_output_init +root usb_console_output_fini +root usb_console_output_enter +root usb_console_output_exit +root usb_console_write +root usb_console_input_enter +root usb_console_input_exit +root usb_console_input_fini +root usb_console_input_init +root usb_console_read +root usb_get_dev_descr +root usb_get_if_number +root usb_parse_CV_cfg_descr +root usb_parse_CV_ep_descr +root usb_parse_CV_if_descr +root usb_parse_comp_ep_descr +root usb_pipe_get_private +root usb_get_current_frame_number +root usb_get_max_isoc_pkts +root usb_pipe_set_private +root usba_ready_interface_node +root usba_free_hcdi_ops +root usba_async_req_raise_power +root usba_async_req_lower_power +root usb_req_raise_power +root usb_req_lower_power +root usb_set_device_pwrlvl0 +root usb_set_device_pwrlvl1 +root usb_set_device_pwrlvl2 +root usb_set_device_pwrlvl3 +root usb_is_pm_enabled +root usb_async_req +root usb_pipe_bulk_transfer_size +root usb_get_ep_data +root usba_pipe_get_policy +root usb_pipe_ctrl_xfer_wait +root usb_pipe_drain_reqs +root usb_try_serialize_access +root usb_clr_feature +root usb_clear_feature +root usb_get_alt_if +root usb_get_ep_descr +root usb_get_if_descr +root usb_log +root usb_pipe_isoc_xfer +root usb_pipe_stop_isoc_polling +root usb_set_alt_if +root usb_set_cfg +root usb_get_cfg +root usb_ep_num +root usb_get_status +root usb_pipe_reset +root usb_log_descr_tree +root usb_print_descr_tree +root usb_rval2errno +root usb_register_hotplug_cbs +root usb_get_current_cfgidx +root usb_register_client +root usb_reset_device +root usb_ugen_power +root usb_ugen_attach +root usb_ugen_close +root usb_ugen_detach +root usb_ugen_disconnect_ev_cb +root usb_ugen_get_hdl +root usb_ugen_open +root usb_ugen_poll +root usb_ugen_read +root usb_ugen_reconnect_ev_cb +root usb_ugen_write +root usb_register_dev_driver +root usb_unregister_dev_driver + +root hcdi_autoclearing +root hcdi_cb_thread +root hcdi_shared_cb_thread + + +root hubd_restore_state_cb +root hubd_disconnect_event_cb +root hubd_post_resume_event_cb +root hubd_pre_suspend_event_cb +root hubd_reconnect_event_cb +root hubd_hotplug_thread +root hubd_reset_thread +root hubd_cpr_post_user_callb +root hubd_root_hub_cleanup_thread +root hubd_bus_power + +root usba_common_power +root usba_common_register_events +root usba_ready_interface_association_node +root usba_pipe_do_async_func_thread +root usba_pipe_sync_reset +root usba_get_hc_dma_attr +root usba_hcdi_get_req_private +root usba_hcdi_set_req_private +root usba_dbuf_tail +root usba_hubdi_power +root usba_hubdi_root_hub_power +root usba_get_hotplug_stats +root usba_reset_hotplug_stats +root usba_ascii_string_descr +root usba_move_list +root usba_taskq_destroy +root usba_mk_mctl +root usb_fail_checkpoint + +root ohci_intr +root ehci_intr +root ohci_quiesce +root uhci_quiesce +root ehci_quiesce + +### specify the usbecm root functions +root usbecm_power +root usbecm_disconnect_event_cb +root usbecm_reconnect_event_cb + +root usbecm_m_stop +root usbecm_m_start +root usbecm_m_stat +root usbecm_m_promisc +root usbecm_m_multicst +root usbecm_m_unicst +root usbecm_m_tx +root usbecm_m_ioctl +root usbecm_m_getprop +root usbecm_m_setprop + +# temporary +root usbecm_pm_set_busy +root usbecm_pm_set_idle + +add hubd::h_cleanup_child targets warlock_dummy +add usb_ctrl_req::ctrl_cb targets warlock_dummy +add usb_ctrl_req::ctrl_exc_cb targets warlock_dummy +add usba_pipe_async_req::callback targets warlock_dummy +add usba_pipe_async_req::sync_func targets warlock_dummy +add usba_pm_req::cb targets warlock_dummy + +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_ctrl_td +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_bulk_td +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_intr_td +add ohci_trans_wrapper::tw_handle_td targets ohci_handle_isoc_td + +add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_bulk_qtd +add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_intr_qtd +add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_ctrl_qtd + +add usbecm_ds_ops::ecm_ds_init targets warlock_dummy +add usbecm_ds_ops::ecm_ds_fini targets warlock_dummy +add usbecm_ds_ops::ecm_ds_start targets warlock_dummy +add usbecm_ds_ops::ecm_ds_stop targets warlock_dummy +add usbecm_ds_ops::ecm_ds_intr_cb targets warlock_dummy
--- a/usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h Thu Mar 11 16:01:06 2010 -0800 +++ b/usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h Fri Mar 12 09:19:50 2010 +0800 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,7 +29,6 @@ #include <sys/types.h> #include <sys/dditypes.h> -#include <sys/usb/clients/usbser/usbser_dsdi.h> #ifdef __cplusplus extern "C" { @@ -96,6 +95,19 @@ /* more slave interafce may follow */ } usb_cdc_union_descr_t; +/* Ethernet Control Model Functional Descriptor */ +typedef struct usb_cdc_ecm_descr { + uint8_t bFunctionalLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t iMACAddress; + uint32_t bmEthernetStatistics; + uint16_t wMaxSegmentSize; + uint16_t wNumberMCFilters; + uint8_t bNumberPowerFilters; +} usb_cdc_ecm_descr_t; + + /* * Class-specific requests */ @@ -115,6 +127,7 @@ } usb_cdc_line_coding_t; #define USB_CDC_LINE_CODING_LEN 7 +#define USB_CDC_ECM_LEN 13 #define USB_CDC_STOP_BITS_1 0 #define USB_CDC_STOP_BITS_1_5 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/sys/usb/clients/usbecm/usbecm.h Fri Mar 12 09:19:50 2010 +0800 @@ -0,0 +1,277 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_USB_USBETH_H +#define _SYS_USB_USBETH_H + + +#include <sys/types.h> +#include <sys/dditypes.h> +#include <sys/mac.h> +#include <sys/note.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct usbecm_state usbecm_state_t; + + +/* + * PM support + */ +typedef struct usbecm_power { + uint8_t pm_wakeup_enabled; /* remote wakeup enabled */ + uint8_t pm_pwr_states; /* bit mask of power states */ + boolean_t pm_raise_power; /* driver is about to raise power */ + uint8_t pm_cur_power; /* current power level */ + uint_t pm_busy_cnt; /* number of set_busy requests */ +} usbecm_pm_t; + +struct usbecm_statistics { + uint32_t es_upspeed; /* Upstream bit rate, bps */ + uint32_t es_downspeed; /* Downstream bit rate, bps */ + int es_linkstate; /* link state */ + uint64_t es_ipackets; + uint64_t es_opackets; + uint64_t es_ibytes; + uint64_t es_obytes; + uint64_t es_ierrors; /* received frames with errors */ + uint64_t es_oerrors; /* transmitted frames with errors */ + uint64_t es_multircv; /* received multicast frames */ + uint64_t es_multixmt; /* transmitted multicast frames */ + uint64_t es_brdcstrcv; + uint64_t es_brdcstxmt; + uint64_t es_macxmt_err; +}; + +struct usbecm_ds_ops { + /* Device specific initialization and deinitialization */ + int (*ecm_ds_init)(usbecm_state_t *); + int (*ecm_ds_fini)(usbecm_state_t *); + + int (*ecm_ds_start)(usbecm_state_t *); + int (*ecm_ds_stop)(usbecm_state_t *); + int (*ecm_ds_unicst)(usbecm_state_t *); + int (*ecm_ds_promisc)(usbecm_state_t *); + int (*ecm_ds_multicst)(usbecm_state_t *); + mblk_t *(*ecm_ds_tx)(usbecm_state_t *, mblk_t *); + + int (*ecm_ds_intr_cb)(usbecm_state_t *, mblk_t *); + int (*ecm_ds_bulkin_cb)(usbecm_state_t *, mblk_t *); + int (*ecm_ds_bulkout_cb)(usbecm_state_t *, mblk_t *); +}; + +/* + * per bulk in/out structure + */ +struct usbecm_state { + kmutex_t ecm_mutex; /* structure lock */ + dev_info_t *ecm_dip; /* device info */ + usb_client_dev_data_t *ecm_dev_data; /* registration data */ + usb_pipe_handle_t ecm_def_ph; /* default pipe hdl */ + usb_log_handle_t ecm_lh; /* USBA log handle */ + int ecm_dev_state; /* USB device state */ + size_t ecm_xfer_sz; /* bulk xfer size */ + size_t ecm_bulkin_sz; + usbecm_pm_t *ecm_pm; /* PM support */ + mac_handle_t ecm_mh; /* mac handle */ + usb_serialization_t ecm_ser_acc; /* serialization object */ + + uint_t ecm_cfg_index; /* config contains ECM ifc */ + uint16_t ecm_ctrl_if_no; + uint16_t ecm_data_if_no; + uint16_t ecm_data_if_alt; /* non-compatible device */ + + usb_ep_data_t *ecm_intr_ep; + usb_ep_data_t *ecm_bulk_in_ep; + usb_ep_data_t *ecm_bulk_out_ep; + + boolean_t ecm_compatibility; /* if conform to spec */ + usb_cdc_ecm_descr_t ecm_desc; /* if conform to spec */ + + uint8_t ecm_srcaddr[6]; /* source MAC addr */ + uint16_t ecm_pkt_flt; /* pkt flt bitmap ECM1.2 T.8 */ + + usb_pipe_handle_t ecm_bulkout_ph; + int ecm_bulkout_state; + usb_pipe_handle_t ecm_bulkin_ph; + int ecm_bulkin_state; + usb_pipe_handle_t ecm_intr_ph; + int ecm_intr_state; + struct usbecm_statistics ecm_stat; + uint32_t ecm_init_flags; + int ecm_mac_state; + mblk_t *ecm_rcv_queue; /* receive queue */ + int ecm_tx_cnt; + + void *ecm_priv; /* device private data */ + struct usbecm_ds_ops *ecm_ds_ops; +}; + + +_NOTE(MUTEX_PROTECTS_DATA(usbecm_state::ecm_mutex, usbecm_state)) +_NOTE(MUTEX_PROTECTS_DATA(usbecm_state::ecm_mutex, usbecm_statistics)) + +_NOTE(DATA_READABLE_WITHOUT_LOCK(usbecm_state::{ + ecm_dip + ecm_dev_data + ecm_def_ph + ecm_lh + ecm_dev_state + ecm_xfer_sz + ecm_compatibility + ecm_pm + ecm_mh + ecm_bulkin_ph + ecm_bulkout_ph + ecm_intr_ph + ecm_ser_acc + ecm_ctrl_if_no + ecm_data_if_no + ecm_data_if_alt + ecm_desc + ecm_bulk_in_ep + ecm_intr_ep + ecm_bulk_out_ep + ecm_bulkin_sz + ecm_priv + ecm_ds_ops +})) + +_NOTE(SCHEME_PROTECTS_DATA("unshared data", mblk_t iocblk)) +_NOTE(SCHEME_PROTECTS_DATA("unshared data", usb_bulk_req_t usb_intr_req_t)) + +/* pipe state */ +enum { + USBECM_PIPE_CLOSED, /* pipe is closed */ + USBECM_PIPE_IDLE, /* open but no requests */ + USBECM_PIPE_BUSY, /* servicing request */ + USBECM_PIPE_CLOSING /* pipe is closing */ +}; + +enum { + USBECM_MAC_STOPPED = 0, + USBECM_MAC_STARTED, +}; + +/* various tunables */ +enum { + USBECM_BULKOUT_TIMEOUT = 15, /* bulkout timeout */ + USBECM_BULKIN_TIMEOUT = 0 /* bulkin timeout */ +}; + +/* hardware definitions */ +enum { + USBSACM_REQ_OUT = USB_DEV_REQ_TYPE_CLASS| USB_DEV_REQ_HOST_TO_DEV, + USBSACM_REQ_IN = USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_DEV_TO_HOST, + USBSACM_REQ_WRITE_IF = USBSACM_REQ_OUT | USB_DEV_REQ_RCPT_IF, + USBSACM_REQ_READ_IF = USBSACM_REQ_IN | USB_DEV_REQ_RCPT_IF +}; + +#define USBECM_INIT_EVENTS (0x01 << 0) +#define USBECM_INIT_SER (0x01 << 1) +#define USBECM_INIT_MAC (0x01 << 2) + +/* Bit offset for ECM statistics capabilities, CDC ECM Rev 1.2, Table 4 */ +#define ECM_XMIT_OK 0 +#define ECM_RCV_OK 1 +#define ECM_XMIT_ERROR 2 +#define ECM_RCV_ERROR 3 +#define ECM_RCV_NO_BUFFER 4 +#define ECM_DIRECTED_BYTES_XMIT 5 +#define ECM_DIRECTED_FRAMES_XMIT 6 +#define ECM_MULTICAST_BYTES_XMIT 7 +#define ECM_MULTICAST_FRAMES_XMIT 8 +#define ECM_BROADCAST_BYTES_XMIT 9 +#define ECM_BROADCAST_FRAMES_XMIT 10 +#define ECM_DIRECTED_BYTES_RCV 11 +#define ECM_DIRECTED_FRAMES_RCV 12 +#define ECM_MULTICAST_BYTES_RCV 13 +#define ECM_MULTICAST_FRAMES_RCV 14 +#define ECM_BROADCAST_BYTES_RCV 15 +#define ECM_BROADCAST_FRAMES_RCV 16 +#define ECM_RCV_CRC_ERROR 17 +#define ECM_TRANSMIT_QUEUE_LENGTH 18 +#define ECM_RCV_ERROR_ALIGNMENT 19 +#define ECM_XMIT_ONE_COLLISION 20 +#define ECM_XMIT_MORE_COLLISIONS 21 +#define ECM_XMIT_DEFERRED 22 +#define ECM_XMIT_MAX_COLLISIONS 23 +#define ECM_RCV_OVERRUN 24 +#define ECM_XMIT_UNDERRUN 25 +#define ECM_XMIT_HEARTBEAT_FAILURE 26 +#define ECM_XMIT_TIMES_CRS_LOST 27 +#define ECM_XMIT_LATE_COLLISIONS 28 + +#define ECM_STAT_CAP_MASK(x) (1UL << (x)) /* Table 4 */ +#define ECM_STAT_SELECTOR(x) ((x) + 1) /* Table 9 */ + +/* ECM class-specific request codes, Table 6 */ +#define CDC_ECM_SET_ETH_MCAST_FLT 0x40 +#define CDC_ECM_SET_ETH_PM_FLT 0x41 +#define CDC_ECM_GET_ETH_PM_FLT 0x42 +#define CDC_ECM_SET_ETH_PKT_FLT 0x43 +#define CDC_ECM_GET_ETH_STAT 0x44 + +/* ECM Ethernet Pakcet Filter Bitmap, Table 8 */ +#define CDC_ECM_PKT_TYPE_PROMISC (1<<0) +#define CDC_ECM_PKT_TYPE_ALL_MCAST (1<<1) /* all multicast */ +#define CDC_ECM_PKT_TYPE_DIRECTED (1<<2) +#define CDC_ECM_PKT_TYPE_BCAST (1<<3) /* broadcast */ +#define CDC_ECM_PKT_TYPE_MCAST (1<<4) /* multicast */ + +#define PRINT_MASK_ATTA 0x00000001 +#define PRINT_MASK_CLOSE 0x00000002 +#define PRINT_MASK_OPEN 0x00000004 +#define PRINT_MASK_EVENTS 0x00000008 +#define PRINT_MASK_PM 0x00000010 +#define PRINT_MASK_CB 0x00000020 +#define PRINT_MASK_OPS 0x00000040 +#define PRINT_MASK_ALL 0xFFFFFFFF + +/* Turn a little endian byte array to a uint32_t */ +#define LE_TO_UINT32(src, des) { \ + uint32_t tmp; \ + des = src[3]; \ + des = des << 24; \ + tmp = src[2]; \ + des |= tmp << 16; \ + tmp = src[1]; \ + des |= tmp << 8; \ + des |= src[0]; \ +} + +#define isdigit(c) ((c) >= '0' && c <= '9') +#define toupper(C) (((C) >= 'a' && (C) <= 'z')? ((C) - 'a' + 'A'): (C)) + +/* #define NELEM(a) (sizeof (a) / sizeof (*(a))) */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_USB_USBETH_H */
--- a/usr/src/uts/intel/Makefile.intel.shared Thu Mar 11 16:01:06 2010 -0800 +++ b/usr/src/uts/intel/Makefile.intel.shared Fri Mar 12 09:19:50 2010 +0800 @@ -484,6 +484,7 @@ DRV_KMODS += usbftdi DRV_KMODS += wusb_df DRV_KMODS += wusb_ca +DRV_KMODS += usbecm $(CLOSED_BUILD)CLOSED_DRV_KMODS += usbser_edge
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/usbecm/Makefile Fri Mar 12 09:19:50 2010 +0800 @@ -0,0 +1,132 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# uts/intel/usbecm/Makefile +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +# This makefile drives the production of Abstract Control Model of +# USB Communication Devices Class dirver. +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = usbecm +OBJECTS = $(USBECM_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(USBECM_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +WARLOCK_OUT = $(USBECM_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +LDFLAGS += -dy -Nmisc/usba -Nmisc/mac + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +.KEEP_STATE: + +all: $(ALL_DEPS) + +def: $(DEF_DEPS) + +clean: $(CLEAN_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +clobber: $(CLOBBER_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ + +# +# Defines for local commands. +# +WLCC = wlcc +TOUCH = touch +WARLOCK = warlock +TEST = test + +# +# warlock +# +WARLOCK_CMD = $(WLCMD_DIR)/$(MODULE).wlcmd + +USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll) +UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll) +OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll) +EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll) + +warlock: $(WARLOCK_OK) warlock_with_usba + +%.ll: $(UTSBASE)/common/io/usb/clients/usbecm/%.c + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +$(WARLOCK_OK): $(WARLOCK_OUT) $(WLCMD_DIR)/usbecm.wlcmd warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usbecm.wlcmd $(WARLOCK_OUT) \ + -l ../warlock/ddi_dki_impl.ll + $(TOUCH) $@ + +warlock_with_usba: $(WLCMD_DIR)/usbecm_with_usba.wlcmd $(WARLOCK_OUT) \ + usba_files ohci_files uhci_files ehci_files \ + warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usbecm_with_usba.wlcmd \ + $(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \ + $(WARLOCK_OUT) -l ../warlock/ddi_dki_impl.ll + +usba_files: + @cd ../usba;pwd; $(MAKE) warlock + +uhci_files: + @cd ../uhci;pwd; $(MAKE) warlock + +ohci_files: + @cd ../ohci;pwd; $(MAKE) warlock + +ehci_files: + @cd ../ehci;pwd; $(MAKE) warlock + +warlock_ddi.files: + cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/intel/warlock/Makefile Thu Mar 11 16:01:06 2010 -0800 +++ b/usr/src/uts/intel/warlock/Makefile Fri Mar 12 09:19:50 2010 +0800 @@ -20,7 +20,7 @@ # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/intel/warlock/Makefile @@ -79,6 +79,7 @@ @cd ../usbsksp; $(MAKE) clean; $(MAKE) warlock @cd ../usbsprl; $(MAKE) clean; $(MAKE) warlock @cd ../usbsacm; $(MAKE) clean; $(MAKE) warlock + @cd ../usbecm; $(MAKE) clean; $(MAKE) warlock @cd ../usbskel; $(MAKE) clean; $(MAKE) warlock $(CLOSED_BUILD) @cd $(CLOSED)/uts/intel/usbser_edge; \ $(MAKE) clean; $(MAKE) warlock
--- a/usr/src/uts/sparc/Makefile.sparc.shared Thu Mar 11 16:01:06 2010 -0800 +++ b/usr/src/uts/sparc/Makefile.sparc.shared Fri Mar 12 09:19:50 2010 +0800 @@ -279,6 +279,7 @@ DRV_KMODS += usbvc DRV_KMODS += usbftdi DRV_KMODS += wusb_df hwahc hwarc wusb_ca +DRV_KMODS += usbecm DRV_KMODS += hci1394 av1394 scsa1394 dcam1394 DRV_KMODS += sbp2 DRV_KMODS += ib ibd rdsib sdp iser daplt hermon tavor
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sparc/usbecm/Makefile Fri Mar 12 09:19:50 2010 +0800 @@ -0,0 +1,137 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# uts/sparc/usbecm/Makefile +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +# This makefile drives the production of Abstract Control Model of +# USB Communication Devices Class dirver. +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = usbecm +OBJECTS = $(USBECM_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(USBECM_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +WARLOCK_OUT = $(USBECM_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok +WLCMD_DIR = $(UTSBASE)/common/io/warlock + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +LDFLAGS += -dy -Nmisc/usba -Nmisc/mac + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +.KEEP_STATE: + +all: $(ALL_DEPS) + +def: $(DEF_DEPS) + +clean: $(CLEAN_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +clobber: $(CLOBBER_DEPS) + $(RM) $(WARLOCK_OUT) $(WARLOCK_OK) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ + +# +# Defines for local commands. +# +WLCC = wlcc +TOUCH = touch +WARLOCK = warlock +TEST = test + +# +# warlock +# +WARLOCK_CMD = $(WLCMD_DIR)/$(MODULE).wlcmd + +USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll) +UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll) +OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll) +EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll) + +warlock: $(WARLOCK_OK) warlock_with_usba + +%.ll: $(UTSBASE)/common/io/usb/clients/usbecm/%.c + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +$(WARLOCK_OK): $(WARLOCK_OUT) $(WLCMD_DIR)/usbecm.wlcmd warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usbecm.wlcmd $(WARLOCK_OUT) \ + -l ../warlock/ddi_dki_impl.ll + $(TOUCH) $@ + +warlock_with_usba: $(WLCMD_DIR)/usbecm_with_usba.wlcmd $(WARLOCK_OUT) \ + usba_files ohci_files uhci_files ehci_files \ + warlock_ddi.files + $(WARLOCK) -c $(WLCMD_DIR)/usbecm_with_usba.wlcmd \ + $(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \ + $(WARLOCK_OUT) -l ../warlock/ddi_dki_impl.ll + +usba_files: + @cd ../usba;pwd; $(MAKE) warlock + +uhci_files: + @cd ../uhci;pwd; $(MAKE) warlock + +ohci_files: + @cd ../ohci;pwd; $(MAKE) warlock + +ehci_files: + @cd ../ehci;pwd; $(MAKE) warlock + +warlock_ddi.files: + cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/sparc/warlock/Makefile Thu Mar 11 16:01:06 2010 -0800 +++ b/usr/src/uts/sparc/warlock/Makefile Fri Mar 12 09:19:50 2010 +0800 @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # sparc architecture dependent @@ -80,6 +80,7 @@ @cd ../usbsksp; $(MAKE) clean; $(MAKE) warlock @cd ../usbsprl; $(MAKE) clean; $(MAKE) warlock @cd ../usbsacm; $(MAKE) clean; $(MAKE) warlock + @cd ../usbecm; $(MAKE) clean; $(MAKE) warlock @cd ../usbskel; $(MAKE) clean; $(MAKE) warlock $(CLOSED_BUILD) @cd $(CLOSED)/uts/sparc/usbser_edge; \ $(MAKE) clean; $(MAKE) warlock