111900SRaymond.Chen@Sun.COM /*
211900SRaymond.Chen@Sun.COM * CDDL HEADER START
311900SRaymond.Chen@Sun.COM *
411900SRaymond.Chen@Sun.COM * The contents of this file are subject to the terms of the
511900SRaymond.Chen@Sun.COM * Common Development and Distribution License (the "License").
611900SRaymond.Chen@Sun.COM * You may not use this file except in compliance with the License.
711900SRaymond.Chen@Sun.COM *
811900SRaymond.Chen@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911900SRaymond.Chen@Sun.COM * or http://www.opensolaris.org/os/licensing.
1011900SRaymond.Chen@Sun.COM * See the License for the specific language governing permissions
1111900SRaymond.Chen@Sun.COM * and limitations under the License.
1211900SRaymond.Chen@Sun.COM *
1311900SRaymond.Chen@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1411900SRaymond.Chen@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511900SRaymond.Chen@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1611900SRaymond.Chen@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1711900SRaymond.Chen@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1811900SRaymond.Chen@Sun.COM *
1911900SRaymond.Chen@Sun.COM * CDDL HEADER END
2011900SRaymond.Chen@Sun.COM */
2111900SRaymond.Chen@Sun.COM
2211900SRaymond.Chen@Sun.COM /*
2311900SRaymond.Chen@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2411900SRaymond.Chen@Sun.COM * Use is subject to license terms.
2511900SRaymond.Chen@Sun.COM */
2611900SRaymond.Chen@Sun.COM
2711900SRaymond.Chen@Sun.COM /*
2811900SRaymond.Chen@Sun.COM * USB Ethernet Control Model
2911900SRaymond.Chen@Sun.COM *
3011900SRaymond.Chen@Sun.COM * USB-IF defines three ethernet network related specifications: EEM,
3111900SRaymond.Chen@Sun.COM * ECM and NCM. This driver focuses specifically on ECM compatible
3211900SRaymond.Chen@Sun.COM * devices. This kind of devices generally have one pair of bulk
3311900SRaymond.Chen@Sun.COM * endpoints for in/out packet data and one interrupt endpoint for
3411900SRaymond.Chen@Sun.COM * device notification.
3511900SRaymond.Chen@Sun.COM *
3611900SRaymond.Chen@Sun.COM * Devices which don't report ECM compatibility through descriptors but
3711900SRaymond.Chen@Sun.COM * implement the ECM functions may also bind to this driver. This driver
3811900SRaymond.Chen@Sun.COM * will try to find at least a bulk in endpoint and a bulk out endpoint
3911900SRaymond.Chen@Sun.COM * in this case. If the non-compatible devices use vendor specific data
4011900SRaymond.Chen@Sun.COM * format, this driver will not function.
4111900SRaymond.Chen@Sun.COM *
4211900SRaymond.Chen@Sun.COM * This driver is a normal USBA client driver. It's also a GLDv3 driver,
4311900SRaymond.Chen@Sun.COM * which provides the necessary interfaces the GLDv3 framework requires.
4411900SRaymond.Chen@Sun.COM *
4511900SRaymond.Chen@Sun.COM */
4611900SRaymond.Chen@Sun.COM
4711900SRaymond.Chen@Sun.COM #include <sys/types.h>
4811900SRaymond.Chen@Sun.COM #include <sys/strsun.h>
4911900SRaymond.Chen@Sun.COM #include <sys/ddi.h>
5011900SRaymond.Chen@Sun.COM #include <sys/sunddi.h>
5111900SRaymond.Chen@Sun.COM #include <sys/byteorder.h>
5211900SRaymond.Chen@Sun.COM #include <sys/usb/usba/usbai_version.h>
5311900SRaymond.Chen@Sun.COM #include <sys/usb/usba.h>
5411900SRaymond.Chen@Sun.COM #include <sys/usb/usba/usba_types.h>
5511900SRaymond.Chen@Sun.COM #include <sys/usb/clients/usbcdc/usb_cdc.h>
5611900SRaymond.Chen@Sun.COM #include <sys/usb/clients/usbecm/usbecm.h>
5711900SRaymond.Chen@Sun.COM #include <sys/mac_provider.h>
5811900SRaymond.Chen@Sun.COM #include <sys/strsubr.h>
5911900SRaymond.Chen@Sun.COM #include <sys/ethernet.h>
6011900SRaymond.Chen@Sun.COM #include <sys/mac_ether.h> /* MAC_PLUGIN_IDENT_ETHER */
6111900SRaymond.Chen@Sun.COM #include <sys/random.h> /* random_get_bytes */
6211900SRaymond.Chen@Sun.COM #include <sys/sdt.h> /* sdt */
6311900SRaymond.Chen@Sun.COM #include <inet/nd.h>
6411900SRaymond.Chen@Sun.COM
6511900SRaymond.Chen@Sun.COM /* MAC callbacks */
6611900SRaymond.Chen@Sun.COM static int usbecm_m_stat(void *arg, uint_t stat, uint64_t *val);
6711900SRaymond.Chen@Sun.COM static int usbecm_m_start(void *arg);
6811900SRaymond.Chen@Sun.COM static void usbecm_m_stop(void *arg);
6911900SRaymond.Chen@Sun.COM static int usbecm_m_unicst(void *arg, const uint8_t *macaddr);
7011900SRaymond.Chen@Sun.COM static int usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m);
7111900SRaymond.Chen@Sun.COM static int usbecm_m_promisc(void *arg, boolean_t on);
7211900SRaymond.Chen@Sun.COM static void usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp);
7311900SRaymond.Chen@Sun.COM static mblk_t *usbecm_m_tx(void *arg, mblk_t *mp);
7411900SRaymond.Chen@Sun.COM static int usbecm_m_getprop(void *arg, const char *pr_name,
7511900SRaymond.Chen@Sun.COM mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf);
7611900SRaymond.Chen@Sun.COM static int usbecm_m_setprop(void *arg, const char *pr_name,
7711900SRaymond.Chen@Sun.COM mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf);
7811900SRaymond.Chen@Sun.COM
7911900SRaymond.Chen@Sun.COM static int usbecm_usb_init(usbecm_state_t *ecmp);
8011900SRaymond.Chen@Sun.COM static int usbecm_mac_init(usbecm_state_t *ecmp);
8111900SRaymond.Chen@Sun.COM static int usbecm_mac_fini(usbecm_state_t *ecmp);
8211900SRaymond.Chen@Sun.COM
8311900SRaymond.Chen@Sun.COM
8411900SRaymond.Chen@Sun.COM /* utils */
8511900SRaymond.Chen@Sun.COM static void generate_ether_addr(uint8_t *mac_addr);
8611900SRaymond.Chen@Sun.COM static int usbecm_rx_start(usbecm_state_t *ecmp);
8711900SRaymond.Chen@Sun.COM
8811900SRaymond.Chen@Sun.COM static void usbecm_pipe_start_polling(usbecm_state_t *ecmp);
8911900SRaymond.Chen@Sun.COM static void usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
9011900SRaymond.Chen@Sun.COM static void usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
9111900SRaymond.Chen@Sun.COM static void usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data);
9211900SRaymond.Chen@Sun.COM
9311900SRaymond.Chen@Sun.COM static int usbecm_reconnect_event_cb(dev_info_t *dip);
9411900SRaymond.Chen@Sun.COM static int usbecm_disconnect_event_cb(dev_info_t *dip);
9511900SRaymond.Chen@Sun.COM
9611900SRaymond.Chen@Sun.COM static int usbecm_open_pipes(usbecm_state_t *ecmp);
9711900SRaymond.Chen@Sun.COM static void usbecm_close_pipes(usbecm_state_t *ecmp);
9811900SRaymond.Chen@Sun.COM
9911900SRaymond.Chen@Sun.COM static int usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
10011900SRaymond.Chen@Sun.COM uint16_t value, mblk_t **data, int len);
10111900SRaymond.Chen@Sun.COM static int usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
10211900SRaymond.Chen@Sun.COM uint16_t value, mblk_t **data);
10311900SRaymond.Chen@Sun.COM static int usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data);
10411900SRaymond.Chen@Sun.COM static int usbecm_send_zero_data(usbecm_state_t *ecmp);
10511900SRaymond.Chen@Sun.COM static int usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs,
10611900SRaymond.Chen@Sun.COM uint32_t *stat_data);
10711900SRaymond.Chen@Sun.COM
10811900SRaymond.Chen@Sun.COM static int usbecm_create_pm_components(usbecm_state_t *ecmp);
10911900SRaymond.Chen@Sun.COM static void usbecm_destroy_pm_components(usbecm_state_t *ecmp);
11011900SRaymond.Chen@Sun.COM static int usbecm_power(dev_info_t *dip, int comp, int level);
11111900SRaymond.Chen@Sun.COM static void usbecm_pm_set_busy(usbecm_state_t *ecmp);
11211900SRaymond.Chen@Sun.COM static void usbecm_pm_set_idle(usbecm_state_t *ecmp);
11311900SRaymond.Chen@Sun.COM
11411900SRaymond.Chen@Sun.COM static int usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
11511900SRaymond.Chen@Sun.COM static int usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
11611900SRaymond.Chen@Sun.COM
11711900SRaymond.Chen@Sun.COM static int usbecm_suspend(usbecm_state_t *ecmp);
11811900SRaymond.Chen@Sun.COM static int usbecm_resume(usbecm_state_t *ecmp);
11911900SRaymond.Chen@Sun.COM static int usbecm_restore_device_state(usbecm_state_t *ecmp);
12011900SRaymond.Chen@Sun.COM static void usbecm_cleanup(usbecm_state_t *ecmp);
12111900SRaymond.Chen@Sun.COM
12211900SRaymond.Chen@Sun.COM /* Driver identification */
12311900SRaymond.Chen@Sun.COM static char usbecm_ident[] = "usbecm 1.0";
12411900SRaymond.Chen@Sun.COM
12511900SRaymond.Chen@Sun.COM /* Global state pointer for managing per-device soft states */
12611900SRaymond.Chen@Sun.COM void *usbecm_statep;
12711900SRaymond.Chen@Sun.COM
12811900SRaymond.Chen@Sun.COM /* print levels */
12911900SRaymond.Chen@Sun.COM static uint_t usbecm_errlevel = USB_LOG_L3;
13011900SRaymond.Chen@Sun.COM static uint_t usbecm_errmask = 0xffffffff;
13111900SRaymond.Chen@Sun.COM static uint_t usbecm_instance_debug = (uint_t)-1;
13211900SRaymond.Chen@Sun.COM
13311900SRaymond.Chen@Sun.COM /*
13411900SRaymond.Chen@Sun.COM * to prevent upper layers packet flood from exhausting system
13511900SRaymond.Chen@Sun.COM * resources(USBA does not set limitation of requests on a pipe),
13611900SRaymond.Chen@Sun.COM * we set a upper limit for the transfer queue length.
13711900SRaymond.Chen@Sun.COM */
13811900SRaymond.Chen@Sun.COM static int usbecm_tx_max = 32;
13911900SRaymond.Chen@Sun.COM
14011900SRaymond.Chen@Sun.COM #define SUN_SP_VENDOR_ID 0x0430
14111900SRaymond.Chen@Sun.COM #define SUN_SP_PRODUCT_ID 0xa4a2
14211900SRaymond.Chen@Sun.COM
14311900SRaymond.Chen@Sun.COM static uint8_t usbecm_broadcast[ETHERADDRL] = {
14411900SRaymond.Chen@Sun.COM 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
14511900SRaymond.Chen@Sun.COM };
14611900SRaymond.Chen@Sun.COM
14711900SRaymond.Chen@Sun.COM static usb_event_t usbecm_events = {
14811900SRaymond.Chen@Sun.COM usbecm_disconnect_event_cb,
14911900SRaymond.Chen@Sun.COM usbecm_reconnect_event_cb,
15011900SRaymond.Chen@Sun.COM NULL, NULL
15111900SRaymond.Chen@Sun.COM };
15211900SRaymond.Chen@Sun.COM
15311900SRaymond.Chen@Sun.COM #define ECM_DS_OP_VALID(op) ((ecmp->ecm_ds_ops) && (ecmp->ecm_ds_ops->op))
15411900SRaymond.Chen@Sun.COM
15511900SRaymond.Chen@Sun.COM /*
15611900SRaymond.Chen@Sun.COM * MAC Call Back entries
15711900SRaymond.Chen@Sun.COM */
15811900SRaymond.Chen@Sun.COM static mac_callbacks_t usbecm_m_callbacks = {
15911900SRaymond.Chen@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP,
16011900SRaymond.Chen@Sun.COM usbecm_m_stat, /* Get the value of a statistic */
16111900SRaymond.Chen@Sun.COM usbecm_m_start, /* Start the device */
16211900SRaymond.Chen@Sun.COM usbecm_m_stop, /* Stop the device */
16311900SRaymond.Chen@Sun.COM usbecm_m_promisc, /* Enable or disable promiscuous mode */
16411900SRaymond.Chen@Sun.COM usbecm_m_multicst, /* Enable or disable a multicast addr */
16511900SRaymond.Chen@Sun.COM usbecm_m_unicst, /* Set the unicast MAC address */
16611900SRaymond.Chen@Sun.COM usbecm_m_tx, /* Transmit a packet */
16711900SRaymond.Chen@Sun.COM NULL,
16811900SRaymond.Chen@Sun.COM usbecm_m_ioctl, /* Process an unknown ioctl */
16911900SRaymond.Chen@Sun.COM NULL, /* mc_getcapab */
17011900SRaymond.Chen@Sun.COM NULL, /* mc_open */
17111900SRaymond.Chen@Sun.COM NULL, /* mc_close */
17211900SRaymond.Chen@Sun.COM usbecm_m_setprop, /* mc_setprop */
17311900SRaymond.Chen@Sun.COM usbecm_m_getprop, /* mc_getprop */
17411900SRaymond.Chen@Sun.COM NULL
17511900SRaymond.Chen@Sun.COM };
17611900SRaymond.Chen@Sun.COM
17711900SRaymond.Chen@Sun.COM
17811900SRaymond.Chen@Sun.COM /*
17911900SRaymond.Chen@Sun.COM * Module Loading Data & Entry Points
18011900SRaymond.Chen@Sun.COM * Can't use DDI_DEFINE_STREAM_OPS, since it does
18111900SRaymond.Chen@Sun.COM * not provide devo_power entry.
18211900SRaymond.Chen@Sun.COM */
18311900SRaymond.Chen@Sun.COM static struct cb_ops cb_usbecm = {
18411900SRaymond.Chen@Sun.COM nulldev, /* cb_open */
18511900SRaymond.Chen@Sun.COM nulldev, /* cb_close */
18611900SRaymond.Chen@Sun.COM nodev, /* cb_strategy */
18711900SRaymond.Chen@Sun.COM nodev, /* cb_print */
18811900SRaymond.Chen@Sun.COM nodev, /* cb_dump */
18911900SRaymond.Chen@Sun.COM nodev, /* cb_read */
19011900SRaymond.Chen@Sun.COM nodev, /* cb_write */
19111900SRaymond.Chen@Sun.COM nodev, /* cb_ioctl */
19211900SRaymond.Chen@Sun.COM nodev, /* cb_devmap */
19311900SRaymond.Chen@Sun.COM nodev, /* cb_mmap */
19411900SRaymond.Chen@Sun.COM nodev, /* cb_segmap */
19511900SRaymond.Chen@Sun.COM nochpoll, /* cb_chpoll */
19611900SRaymond.Chen@Sun.COM ddi_prop_op, /* cb_prop_op */
19711900SRaymond.Chen@Sun.COM NULL, /* cb_stream */
19811900SRaymond.Chen@Sun.COM D_MP, /* cb_flag */
19911900SRaymond.Chen@Sun.COM CB_REV, /* cb_rev */
20011900SRaymond.Chen@Sun.COM nodev, /* cb_aread */
20111900SRaymond.Chen@Sun.COM nodev, /* cb_awrite */
20211900SRaymond.Chen@Sun.COM };
20311900SRaymond.Chen@Sun.COM
20411900SRaymond.Chen@Sun.COM static struct dev_ops usbecm_devops = {
20511900SRaymond.Chen@Sun.COM DEVO_REV, /* devo_rev */
20611900SRaymond.Chen@Sun.COM 0, /* devo_refcnt */
20711900SRaymond.Chen@Sun.COM NULL, /* devo_getinfo */
20811900SRaymond.Chen@Sun.COM nulldev, /* devo_identify */
20911900SRaymond.Chen@Sun.COM nulldev, /* devo_probe */
21011900SRaymond.Chen@Sun.COM usbecm_attach, /* devo_attach */
21111900SRaymond.Chen@Sun.COM usbecm_detach, /* devo_detach */
21211900SRaymond.Chen@Sun.COM nodev, /* devo_reset */
21311900SRaymond.Chen@Sun.COM &(cb_usbecm), /* devo_cb_ops */
21411900SRaymond.Chen@Sun.COM (struct bus_ops *)NULL, /* devo_bus_ops */
21511900SRaymond.Chen@Sun.COM usbecm_power, /* devo_power */
21611900SRaymond.Chen@Sun.COM ddi_quiesce_not_needed /* devo_quiesce */
21711900SRaymond.Chen@Sun.COM };
21811900SRaymond.Chen@Sun.COM
21911900SRaymond.Chen@Sun.COM static struct modldrv usbecm_modldrv = {
22011900SRaymond.Chen@Sun.COM &mod_driverops, /* drv_modops */
22111900SRaymond.Chen@Sun.COM usbecm_ident, /* drv_linkinfo */
22211900SRaymond.Chen@Sun.COM &usbecm_devops /* drv_dev_ops */
22311900SRaymond.Chen@Sun.COM };
22411900SRaymond.Chen@Sun.COM
22511900SRaymond.Chen@Sun.COM static struct modlinkage usbecm_ml = {
22611900SRaymond.Chen@Sun.COM MODREV_1, /* ml_rev */
22711900SRaymond.Chen@Sun.COM &usbecm_modldrv, NULL /* ml_linkage */
22811900SRaymond.Chen@Sun.COM };
22911900SRaymond.Chen@Sun.COM
23011900SRaymond.Chen@Sun.COM
23111900SRaymond.Chen@Sun.COM /*
23211900SRaymond.Chen@Sun.COM * Device operations
23311900SRaymond.Chen@Sun.COM */
23411900SRaymond.Chen@Sun.COM /*
23511900SRaymond.Chen@Sun.COM * Binding the driver to a device.
23611900SRaymond.Chen@Sun.COM *
23711900SRaymond.Chen@Sun.COM * Concurrency: Until usbecm_attach() returns with success,
23811900SRaymond.Chen@Sun.COM * the only other entry point that can be executed is getinfo().
23911900SRaymond.Chen@Sun.COM * Thus no locking here yet.
24011900SRaymond.Chen@Sun.COM */
24111900SRaymond.Chen@Sun.COM static int
usbecm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)24211900SRaymond.Chen@Sun.COM usbecm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
24311900SRaymond.Chen@Sun.COM {
24411900SRaymond.Chen@Sun.COM char strbuf[32];
24511900SRaymond.Chen@Sun.COM int instance;
24611900SRaymond.Chen@Sun.COM int err;
24711900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = NULL;
24811900SRaymond.Chen@Sun.COM
24911900SRaymond.Chen@Sun.COM switch (cmd) {
25011900SRaymond.Chen@Sun.COM case DDI_ATTACH:
25111900SRaymond.Chen@Sun.COM break;
25211900SRaymond.Chen@Sun.COM
25311900SRaymond.Chen@Sun.COM case DDI_RESUME:
25411900SRaymond.Chen@Sun.COM ecmp = (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
25511900SRaymond.Chen@Sun.COM ddi_get_instance(dip));
25611900SRaymond.Chen@Sun.COM
25711900SRaymond.Chen@Sun.COM (void) usbecm_resume(ecmp);
25811900SRaymond.Chen@Sun.COM
25911900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
26011900SRaymond.Chen@Sun.COM
26111900SRaymond.Chen@Sun.COM default:
26211900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
26311900SRaymond.Chen@Sun.COM }
26411900SRaymond.Chen@Sun.COM
26511900SRaymond.Chen@Sun.COM instance = ddi_get_instance(dip);
26611900SRaymond.Chen@Sun.COM
26711900SRaymond.Chen@Sun.COM if (ddi_soft_state_zalloc(usbecm_statep, instance) == DDI_SUCCESS) {
26811900SRaymond.Chen@Sun.COM ecmp = ddi_get_soft_state(usbecm_statep, instance);
26911900SRaymond.Chen@Sun.COM }
27011900SRaymond.Chen@Sun.COM if (ecmp == NULL) {
27111900SRaymond.Chen@Sun.COM cmn_err(CE_WARN, "usbecm_attach: fail to get soft state");
27211900SRaymond.Chen@Sun.COM
27311900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
27411900SRaymond.Chen@Sun.COM }
27511900SRaymond.Chen@Sun.COM
27611900SRaymond.Chen@Sun.COM ecmp->ecm_dip = dip;
27711900SRaymond.Chen@Sun.COM
27811900SRaymond.Chen@Sun.COM ecmp->ecm_lh = usb_alloc_log_hdl(ecmp->ecm_dip, "usbecm",
27911900SRaymond.Chen@Sun.COM &usbecm_errlevel, &usbecm_errmask, &usbecm_instance_debug, 0);
28011900SRaymond.Chen@Sun.COM
28111900SRaymond.Chen@Sun.COM if (usbecm_usb_init(ecmp) != USB_SUCCESS) {
28211900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
28311900SRaymond.Chen@Sun.COM "usbecm_attach: failed to init usb");
28411900SRaymond.Chen@Sun.COM
28511900SRaymond.Chen@Sun.COM goto fail;
28611900SRaymond.Chen@Sun.COM }
28711900SRaymond.Chen@Sun.COM
28811900SRaymond.Chen@Sun.COM if (ECM_DS_OP_VALID(ecm_ds_init)) {
28911900SRaymond.Chen@Sun.COM if (ecmp->ecm_ds_ops->ecm_ds_init(ecmp) != USB_SUCCESS) {
29011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
29111900SRaymond.Chen@Sun.COM "usbecm_attach: failed to init DS");
29211900SRaymond.Chen@Sun.COM
29311900SRaymond.Chen@Sun.COM goto fail;
29411900SRaymond.Chen@Sun.COM }
29511900SRaymond.Chen@Sun.COM }
29611900SRaymond.Chen@Sun.COM
29711900SRaymond.Chen@Sun.COM if (usbecm_mac_init(ecmp) != DDI_SUCCESS) {
29811900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
29911900SRaymond.Chen@Sun.COM "usbecm_attach: failed to init mac");
30011900SRaymond.Chen@Sun.COM
30111900SRaymond.Chen@Sun.COM goto fail;
30211900SRaymond.Chen@Sun.COM }
30311900SRaymond.Chen@Sun.COM ecmp->ecm_init_flags |= USBECM_INIT_MAC;
30411900SRaymond.Chen@Sun.COM
30511900SRaymond.Chen@Sun.COM /*
30611900SRaymond.Chen@Sun.COM * Create minor node of type usb_net. Not necessary to create
30711900SRaymond.Chen@Sun.COM * DDI_NT_NET since it's created in mac_register(). Otherwise,
30811900SRaymond.Chen@Sun.COM * system will panic.
30911900SRaymond.Chen@Sun.COM */
31011900SRaymond.Chen@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "usbecm%d", instance);
31111900SRaymond.Chen@Sun.COM err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
31211900SRaymond.Chen@Sun.COM instance + 1, "usb_net", 0);
31311900SRaymond.Chen@Sun.COM if (err != DDI_SUCCESS) {
31411900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
31511900SRaymond.Chen@Sun.COM "failed to create minor node");
31611900SRaymond.Chen@Sun.COM
31711900SRaymond.Chen@Sun.COM goto fail;
31811900SRaymond.Chen@Sun.COM }
31911900SRaymond.Chen@Sun.COM
32011900SRaymond.Chen@Sun.COM /* always busy. May change to a more precise PM in future */
32111900SRaymond.Chen@Sun.COM usbecm_pm_set_busy(ecmp);
32211900SRaymond.Chen@Sun.COM
32311900SRaymond.Chen@Sun.COM ddi_report_dev(dip);
32411900SRaymond.Chen@Sun.COM
32511900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
32611900SRaymond.Chen@Sun.COM "usbecm_attach: succeed!");
32711900SRaymond.Chen@Sun.COM
32811900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
32911900SRaymond.Chen@Sun.COM
33011900SRaymond.Chen@Sun.COM fail:
33111900SRaymond.Chen@Sun.COM USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
33211900SRaymond.Chen@Sun.COM "usbecm_attach: Attach fail");
33311900SRaymond.Chen@Sun.COM
33411900SRaymond.Chen@Sun.COM usbecm_cleanup(ecmp);
33511900SRaymond.Chen@Sun.COM ddi_prop_remove_all(dip);
33611900SRaymond.Chen@Sun.COM ddi_soft_state_free(usbecm_statep, instance);
33711900SRaymond.Chen@Sun.COM
33811900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
33911900SRaymond.Chen@Sun.COM
34011900SRaymond.Chen@Sun.COM }
34111900SRaymond.Chen@Sun.COM
34211900SRaymond.Chen@Sun.COM
34311900SRaymond.Chen@Sun.COM /*
34411900SRaymond.Chen@Sun.COM * Detach the driver from a device.
34511900SRaymond.Chen@Sun.COM *
34611900SRaymond.Chen@Sun.COM * Concurrency: Will be called only after a successful attach
34711900SRaymond.Chen@Sun.COM * (and not concurrently).
34811900SRaymond.Chen@Sun.COM */
34911900SRaymond.Chen@Sun.COM static int
usbecm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)35011900SRaymond.Chen@Sun.COM usbecm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
35111900SRaymond.Chen@Sun.COM {
35211900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = NULL;
35311900SRaymond.Chen@Sun.COM int instance;
35411900SRaymond.Chen@Sun.COM
35511900SRaymond.Chen@Sun.COM instance = ddi_get_instance(dip);
35611900SRaymond.Chen@Sun.COM ecmp = ddi_get_soft_state(usbecm_statep, instance);
35711900SRaymond.Chen@Sun.COM ASSERT(ecmp != NULL);
35811900SRaymond.Chen@Sun.COM
35911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
36011900SRaymond.Chen@Sun.COM "usbecm_detach: entry ");
36111900SRaymond.Chen@Sun.COM
36211900SRaymond.Chen@Sun.COM switch (cmd) {
36311900SRaymond.Chen@Sun.COM case DDI_DETACH:
36411900SRaymond.Chen@Sun.COM break;
36511900SRaymond.Chen@Sun.COM
36611900SRaymond.Chen@Sun.COM case DDI_SUSPEND:
36711900SRaymond.Chen@Sun.COM
36811900SRaymond.Chen@Sun.COM return (usbecm_suspend(ecmp));
36911900SRaymond.Chen@Sun.COM
37011900SRaymond.Chen@Sun.COM default:
37111900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
37211900SRaymond.Chen@Sun.COM }
37311900SRaymond.Chen@Sun.COM
37411900SRaymond.Chen@Sun.COM usbecm_pm_set_idle(ecmp);
37511900SRaymond.Chen@Sun.COM
37611900SRaymond.Chen@Sun.COM if (ECM_DS_OP_VALID(ecm_ds_fini)) {
37711900SRaymond.Chen@Sun.COM if (ecmp->ecm_ds_ops->ecm_ds_fini(ecmp) != USB_SUCCESS) {
37811900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
37911900SRaymond.Chen@Sun.COM "usbecm_detach: deinitialize DS fail!");
38011900SRaymond.Chen@Sun.COM
38111900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
38211900SRaymond.Chen@Sun.COM }
38311900SRaymond.Chen@Sun.COM }
38411900SRaymond.Chen@Sun.COM
38511900SRaymond.Chen@Sun.COM if (usbecm_mac_fini(ecmp) != 0) {
38611900SRaymond.Chen@Sun.COM
38711900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
38811900SRaymond.Chen@Sun.COM }
38911900SRaymond.Chen@Sun.COM
39011900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
39111900SRaymond.Chen@Sun.COM "usbecm_detach: exit");
39211900SRaymond.Chen@Sun.COM
39311900SRaymond.Chen@Sun.COM usbecm_cleanup(ecmp);
39411900SRaymond.Chen@Sun.COM ddi_soft_state_free(usbecm_statep, instance);
39511900SRaymond.Chen@Sun.COM
39611900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
39711900SRaymond.Chen@Sun.COM }
39811900SRaymond.Chen@Sun.COM
39911900SRaymond.Chen@Sun.COM
40011900SRaymond.Chen@Sun.COM /*
40111900SRaymond.Chen@Sun.COM * Mac Call Back functions
40211900SRaymond.Chen@Sun.COM */
40311900SRaymond.Chen@Sun.COM
40411900SRaymond.Chen@Sun.COM /*
40511900SRaymond.Chen@Sun.COM * Read device statistic information.
40611900SRaymond.Chen@Sun.COM */
40711900SRaymond.Chen@Sun.COM static int
usbecm_m_stat(void * arg,uint_t stat,uint64_t * val)40811900SRaymond.Chen@Sun.COM usbecm_m_stat(void *arg, uint_t stat, uint64_t *val)
40911900SRaymond.Chen@Sun.COM {
41011900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
41111900SRaymond.Chen@Sun.COM uint32_t stats;
41211900SRaymond.Chen@Sun.COM int rval;
41311900SRaymond.Chen@Sun.COM uint32_t fs;
41411900SRaymond.Chen@Sun.COM
41511900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
41611900SRaymond.Chen@Sun.COM "usbecm_m_stat: entry, stat=%d", stat);
41711900SRaymond.Chen@Sun.COM
41811900SRaymond.Chen@Sun.COM /*
41911900SRaymond.Chen@Sun.COM * Some of the stats are MII specific. We try to
42011900SRaymond.Chen@Sun.COM * resolve all the statistics we understand. If
42111900SRaymond.Chen@Sun.COM * the usb device can't provide it, return ENOTSUP.
42211900SRaymond.Chen@Sun.COM */
42311900SRaymond.Chen@Sun.COM switch (stat) {
42411900SRaymond.Chen@Sun.COM case MAC_STAT_IFSPEED:
42511900SRaymond.Chen@Sun.COM /* return link speed */
42611900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
42711900SRaymond.Chen@Sun.COM if (ecmp->ecm_stat.es_downspeed) {
42811900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_downspeed;
42911900SRaymond.Chen@Sun.COM } else {
43011900SRaymond.Chen@Sun.COM *val = 10 * 1000000ull; /* set a default value */
43111900SRaymond.Chen@Sun.COM }
43211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
43311900SRaymond.Chen@Sun.COM
43411900SRaymond.Chen@Sun.COM return (0);
43511900SRaymond.Chen@Sun.COM case ETHER_STAT_LINK_DUPLEX:
43611900SRaymond.Chen@Sun.COM *val = LINK_DUPLEX_FULL;
43711900SRaymond.Chen@Sun.COM
43811900SRaymond.Chen@Sun.COM return (0);
43911900SRaymond.Chen@Sun.COM
44011900SRaymond.Chen@Sun.COM case ETHER_STAT_SQE_ERRORS:
44111900SRaymond.Chen@Sun.COM *val = 0;
44211900SRaymond.Chen@Sun.COM
44311900SRaymond.Chen@Sun.COM return (0);
44411900SRaymond.Chen@Sun.COM
44511900SRaymond.Chen@Sun.COM /* Map MAC/Ether stats to ECM statistics */
44611900SRaymond.Chen@Sun.COM case MAC_STAT_NORCVBUF:
44711900SRaymond.Chen@Sun.COM fs = ECM_RCV_NO_BUFFER;
44811900SRaymond.Chen@Sun.COM
44911900SRaymond.Chen@Sun.COM break;
45011900SRaymond.Chen@Sun.COM case MAC_STAT_NOXMTBUF:
45111900SRaymond.Chen@Sun.COM fs = ECM_XMIT_ERROR;
45211900SRaymond.Chen@Sun.COM
45311900SRaymond.Chen@Sun.COM break;
45411900SRaymond.Chen@Sun.COM case MAC_STAT_IERRORS:
45511900SRaymond.Chen@Sun.COM fs = ECM_RCV_ERROR;
45611900SRaymond.Chen@Sun.COM
45711900SRaymond.Chen@Sun.COM break;
45811900SRaymond.Chen@Sun.COM case MAC_STAT_OERRORS:
45911900SRaymond.Chen@Sun.COM fs = ECM_XMIT_ERROR;
46011900SRaymond.Chen@Sun.COM
46111900SRaymond.Chen@Sun.COM break;
46211900SRaymond.Chen@Sun.COM case MAC_STAT_RBYTES:
46311900SRaymond.Chen@Sun.COM fs = ECM_DIRECTED_BYTES_RCV;
46411900SRaymond.Chen@Sun.COM
46511900SRaymond.Chen@Sun.COM break;
46611900SRaymond.Chen@Sun.COM case MAC_STAT_IPACKETS:
46711900SRaymond.Chen@Sun.COM fs = ECM_RCV_OK; /* frames */
46811900SRaymond.Chen@Sun.COM
46911900SRaymond.Chen@Sun.COM break;
47011900SRaymond.Chen@Sun.COM case MAC_STAT_OBYTES:
47111900SRaymond.Chen@Sun.COM fs = ECM_DIRECTED_BYTES_XMIT;
47211900SRaymond.Chen@Sun.COM
47311900SRaymond.Chen@Sun.COM break;
47411900SRaymond.Chen@Sun.COM case MAC_STAT_OPACKETS:
47511900SRaymond.Chen@Sun.COM fs = ECM_XMIT_OK; /* frames */
47611900SRaymond.Chen@Sun.COM
47711900SRaymond.Chen@Sun.COM break;
47811900SRaymond.Chen@Sun.COM case MAC_STAT_MULTIRCV:
47911900SRaymond.Chen@Sun.COM fs = ECM_MULTICAST_FRAMES_RCV;
48011900SRaymond.Chen@Sun.COM
48111900SRaymond.Chen@Sun.COM break;
48211900SRaymond.Chen@Sun.COM case MAC_STAT_BRDCSTRCV:
48311900SRaymond.Chen@Sun.COM fs = ECM_BROADCAST_FRAMES_RCV;
48411900SRaymond.Chen@Sun.COM
48511900SRaymond.Chen@Sun.COM break;
48611900SRaymond.Chen@Sun.COM case MAC_STAT_MULTIXMT:
48711900SRaymond.Chen@Sun.COM fs = ECM_MULTICAST_FRAMES_XMIT;
48811900SRaymond.Chen@Sun.COM
48911900SRaymond.Chen@Sun.COM break;
49011900SRaymond.Chen@Sun.COM case MAC_STAT_BRDCSTXMT:
49111900SRaymond.Chen@Sun.COM fs = ECM_BROADCAST_FRAMES_XMIT;
49211900SRaymond.Chen@Sun.COM
49311900SRaymond.Chen@Sun.COM break;
49411900SRaymond.Chen@Sun.COM case MAC_STAT_COLLISIONS:
49511900SRaymond.Chen@Sun.COM fs = ECM_XMIT_MAX_COLLISIONS;
49611900SRaymond.Chen@Sun.COM
49711900SRaymond.Chen@Sun.COM break;
49811900SRaymond.Chen@Sun.COM case MAC_STAT_OVERFLOWS:
49911900SRaymond.Chen@Sun.COM fs = ECM_RCV_OVERRUN;
50011900SRaymond.Chen@Sun.COM
50111900SRaymond.Chen@Sun.COM break;
50211900SRaymond.Chen@Sun.COM case MAC_STAT_UNDERFLOWS:
50311900SRaymond.Chen@Sun.COM fs = ECM_XMIT_UNDERRUN;
50411900SRaymond.Chen@Sun.COM
50511900SRaymond.Chen@Sun.COM break;
50611900SRaymond.Chen@Sun.COM case ETHER_STAT_FCS_ERRORS:
50711900SRaymond.Chen@Sun.COM fs = ECM_RCV_CRC_ERROR;
50811900SRaymond.Chen@Sun.COM
50911900SRaymond.Chen@Sun.COM break;
51011900SRaymond.Chen@Sun.COM case ETHER_STAT_ALIGN_ERRORS:
51111900SRaymond.Chen@Sun.COM fs = ECM_RCV_ERROR_ALIGNMENT;
51211900SRaymond.Chen@Sun.COM
51311900SRaymond.Chen@Sun.COM break;
51411900SRaymond.Chen@Sun.COM case ETHER_STAT_DEFER_XMTS:
51511900SRaymond.Chen@Sun.COM fs = ECM_XMIT_DEFERRED;
51611900SRaymond.Chen@Sun.COM
51711900SRaymond.Chen@Sun.COM break;
51811900SRaymond.Chen@Sun.COM case ETHER_STAT_FIRST_COLLISIONS:
51911900SRaymond.Chen@Sun.COM fs = ECM_XMIT_ONE_COLLISION;
52011900SRaymond.Chen@Sun.COM
52111900SRaymond.Chen@Sun.COM break;
52211900SRaymond.Chen@Sun.COM case ETHER_STAT_MULTI_COLLISIONS:
52311900SRaymond.Chen@Sun.COM fs = ECM_XMIT_MORE_COLLISIONS;
52411900SRaymond.Chen@Sun.COM
52511900SRaymond.Chen@Sun.COM break;
52611900SRaymond.Chen@Sun.COM case ETHER_STAT_TX_LATE_COLLISIONS:
52711900SRaymond.Chen@Sun.COM fs = ECM_XMIT_LATE_COLLISIONS;
52811900SRaymond.Chen@Sun.COM
52911900SRaymond.Chen@Sun.COM break;
53011900SRaymond.Chen@Sun.COM
53111900SRaymond.Chen@Sun.COM default:
53211900SRaymond.Chen@Sun.COM return (ENOTSUP);
53311900SRaymond.Chen@Sun.COM }
53411900SRaymond.Chen@Sun.COM
53511900SRaymond.Chen@Sun.COM /*
53611900SRaymond.Chen@Sun.COM * we need to access device to get required stats,
53711900SRaymond.Chen@Sun.COM * so check device state first
53811900SRaymond.Chen@Sun.COM */
53911900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
54011900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
54111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
54211900SRaymond.Chen@Sun.COM "usbecm_m_stat: device not ONLINE");
54311900SRaymond.Chen@Sun.COM
54411900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
54511900SRaymond.Chen@Sun.COM
54611900SRaymond.Chen@Sun.COM return (EIO);
54711900SRaymond.Chen@Sun.COM }
54811900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
54911900SRaymond.Chen@Sun.COM
55011900SRaymond.Chen@Sun.COM rval = usbecm_get_statistics(ecmp,
55111900SRaymond.Chen@Sun.COM ECM_STAT_SELECTOR(fs), &stats);
55211900SRaymond.Chen@Sun.COM if (rval != USB_SUCCESS) {
55311900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
55411900SRaymond.Chen@Sun.COM switch (stat) {
55511900SRaymond.Chen@Sun.COM case MAC_STAT_IERRORS:
55611900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_ierrors;
55711900SRaymond.Chen@Sun.COM
55811900SRaymond.Chen@Sun.COM break;
55911900SRaymond.Chen@Sun.COM case MAC_STAT_OERRORS:
56011900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_oerrors;
56111900SRaymond.Chen@Sun.COM
56211900SRaymond.Chen@Sun.COM break;
56311900SRaymond.Chen@Sun.COM case MAC_STAT_RBYTES:
56411900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_ibytes;
56511900SRaymond.Chen@Sun.COM
56611900SRaymond.Chen@Sun.COM break;
56711900SRaymond.Chen@Sun.COM case MAC_STAT_IPACKETS:
56811900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_ipackets;
56911900SRaymond.Chen@Sun.COM
57011900SRaymond.Chen@Sun.COM break;
57111900SRaymond.Chen@Sun.COM case MAC_STAT_OBYTES:
57211900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_obytes;
57311900SRaymond.Chen@Sun.COM
57411900SRaymond.Chen@Sun.COM break;
57511900SRaymond.Chen@Sun.COM case MAC_STAT_OPACKETS:
57611900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_opackets;
57711900SRaymond.Chen@Sun.COM
57811900SRaymond.Chen@Sun.COM break;
57911900SRaymond.Chen@Sun.COM case MAC_STAT_MULTIRCV:
58011900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_multircv;
58111900SRaymond.Chen@Sun.COM
58211900SRaymond.Chen@Sun.COM break;
58311900SRaymond.Chen@Sun.COM case MAC_STAT_MULTIXMT:
58411900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_multixmt;
58511900SRaymond.Chen@Sun.COM
58611900SRaymond.Chen@Sun.COM break;
58711900SRaymond.Chen@Sun.COM case MAC_STAT_BRDCSTRCV:
58811900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_brdcstrcv;
58911900SRaymond.Chen@Sun.COM
59011900SRaymond.Chen@Sun.COM break;
59111900SRaymond.Chen@Sun.COM case MAC_STAT_BRDCSTXMT:
59211900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_brdcstxmt;
59311900SRaymond.Chen@Sun.COM
59411900SRaymond.Chen@Sun.COM break;
59511900SRaymond.Chen@Sun.COM case ETHER_STAT_MACXMT_ERRORS:
59611900SRaymond.Chen@Sun.COM *val = ecmp->ecm_stat.es_macxmt_err;
59711900SRaymond.Chen@Sun.COM break;
59811900SRaymond.Chen@Sun.COM default:
59911900SRaymond.Chen@Sun.COM *val = 0;
60011900SRaymond.Chen@Sun.COM
60111900SRaymond.Chen@Sun.COM break;
60211900SRaymond.Chen@Sun.COM }
60311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
60411900SRaymond.Chen@Sun.COM } else {
60511900SRaymond.Chen@Sun.COM *val = stats;
60611900SRaymond.Chen@Sun.COM }
60711900SRaymond.Chen@Sun.COM
60811900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
60911900SRaymond.Chen@Sun.COM "usbecm_m_stat: end");
61011900SRaymond.Chen@Sun.COM
61111900SRaymond.Chen@Sun.COM return (0);
61211900SRaymond.Chen@Sun.COM }
61311900SRaymond.Chen@Sun.COM
61411900SRaymond.Chen@Sun.COM
61511900SRaymond.Chen@Sun.COM /*
61611900SRaymond.Chen@Sun.COM * Start the device:
61711900SRaymond.Chen@Sun.COM * - Set proper altsettings of the data interface
61811900SRaymond.Chen@Sun.COM * - Open status and data endpoints
61911900SRaymond.Chen@Sun.COM * - Start status polling
62011900SRaymond.Chen@Sun.COM * - Get bulk-in ep ready to receive data from ethernet
62111900SRaymond.Chen@Sun.COM *
62211900SRaymond.Chen@Sun.COM * Concurrency: Presumably fully concurrent, must lock.
62311900SRaymond.Chen@Sun.COM */
62411900SRaymond.Chen@Sun.COM static int
usbecm_m_start(void * arg)62511900SRaymond.Chen@Sun.COM usbecm_m_start(void *arg)
62611900SRaymond.Chen@Sun.COM {
62711900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
62811900SRaymond.Chen@Sun.COM int rval;
62911900SRaymond.Chen@Sun.COM
63011900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
63111900SRaymond.Chen@Sun.COM "usbecm_m_start: entry");
63211900SRaymond.Chen@Sun.COM
63311900SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
63411900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
63511900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
63611900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
63711900SRaymond.Chen@Sun.COM "usbecm_m_start: device not online");
63811900SRaymond.Chen@Sun.COM rval = ENODEV;
63911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
64011900SRaymond.Chen@Sun.COM
64111900SRaymond.Chen@Sun.COM goto fail;
64211900SRaymond.Chen@Sun.COM }
64311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
64411900SRaymond.Chen@Sun.COM
64511900SRaymond.Chen@Sun.COM if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
64611900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
64711900SRaymond.Chen@Sun.COM "usbecm_m_start: open pipes fail");
64811900SRaymond.Chen@Sun.COM rval = EIO;
64911900SRaymond.Chen@Sun.COM
65011900SRaymond.Chen@Sun.COM goto fail;
65111900SRaymond.Chen@Sun.COM }
65211900SRaymond.Chen@Sun.COM
65311900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
65411900SRaymond.Chen@Sun.COM if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
65511900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
65611900SRaymond.Chen@Sun.COM "usbecm_m_start: fail to start_rx");
65711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
65811900SRaymond.Chen@Sun.COM rval = EIO;
65911900SRaymond.Chen@Sun.COM
66011900SRaymond.Chen@Sun.COM goto fail;
66111900SRaymond.Chen@Sun.COM }
66211900SRaymond.Chen@Sun.COM ecmp->ecm_mac_state = USBECM_MAC_STARTED;
66311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
66411900SRaymond.Chen@Sun.COM
66511900SRaymond.Chen@Sun.COM /* set the device to receive all multicast/broadcast pkts */
66611900SRaymond.Chen@Sun.COM rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
66711900SRaymond.Chen@Sun.COM CDC_ECM_PKT_TYPE_DIRECTED | CDC_ECM_PKT_TYPE_ALL_MCAST |
66811900SRaymond.Chen@Sun.COM CDC_ECM_PKT_TYPE_BCAST, NULL);
66911900SRaymond.Chen@Sun.COM if (rval != USB_SUCCESS) {
67011900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
67111900SRaymond.Chen@Sun.COM "usbecm_m_start: set packet filters fail,"
67211900SRaymond.Chen@Sun.COM " rval=%d, continue", rval);
67311900SRaymond.Chen@Sun.COM }
67411900SRaymond.Chen@Sun.COM
67511900SRaymond.Chen@Sun.COM if (ECM_DS_OP_VALID(ecm_ds_start)) {
67611900SRaymond.Chen@Sun.COM if (ecmp->ecm_ds_ops->ecm_ds_start(ecmp) != USB_SUCCESS) {
67711900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
67811900SRaymond.Chen@Sun.COM "usbecm_m_start: Can't start hardware");
67911900SRaymond.Chen@Sun.COM
68011900SRaymond.Chen@Sun.COM goto fail;
68111900SRaymond.Chen@Sun.COM }
68211900SRaymond.Chen@Sun.COM }
68311900SRaymond.Chen@Sun.COM
68411900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
68511900SRaymond.Chen@Sun.COM
68611900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
68711900SRaymond.Chen@Sun.COM "usbecm_m_start: end");
68811900SRaymond.Chen@Sun.COM
68911900SRaymond.Chen@Sun.COM /*
69011900SRaymond.Chen@Sun.COM * To mark the link as RUNNING.
69111900SRaymond.Chen@Sun.COM *
69211900SRaymond.Chen@Sun.COM * ECM spec doesn't provide a way for host to get the status
69311900SRaymond.Chen@Sun.COM * of the physical link initiatively. Only the device can
69411900SRaymond.Chen@Sun.COM * report the link state through interrupt endpoints.
69511900SRaymond.Chen@Sun.COM */
69611900SRaymond.Chen@Sun.COM mac_link_update(ecmp->ecm_mh, LINK_STATE_UP);
69711900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
69811900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_linkstate = LINK_STATE_UP;
69911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
70011900SRaymond.Chen@Sun.COM
70111900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
70211900SRaymond.Chen@Sun.COM fail:
70311900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
70411900SRaymond.Chen@Sun.COM
70511900SRaymond.Chen@Sun.COM return (rval);
70611900SRaymond.Chen@Sun.COM }
70711900SRaymond.Chen@Sun.COM
70811900SRaymond.Chen@Sun.COM /*
70911900SRaymond.Chen@Sun.COM * Stop the device.
71011900SRaymond.Chen@Sun.COM */
71111900SRaymond.Chen@Sun.COM static void
usbecm_m_stop(void * arg)71211900SRaymond.Chen@Sun.COM usbecm_m_stop(void *arg)
71311900SRaymond.Chen@Sun.COM {
71411900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
71511900SRaymond.Chen@Sun.COM
71611900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
71711900SRaymond.Chen@Sun.COM "usbecm_m_stop: entry");
71811900SRaymond.Chen@Sun.COM
719*11902SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
72011900SRaymond.Chen@Sun.COM if (ECM_DS_OP_VALID(ecm_ds_stop)) {
72111900SRaymond.Chen@Sun.COM if (ecmp->ecm_ds_ops->ecm_ds_stop(ecmp) != USB_SUCCESS) {
72211900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
72311900SRaymond.Chen@Sun.COM "usbecm_m_stop: fail to stop hardware");
72411900SRaymond.Chen@Sun.COM }
72511900SRaymond.Chen@Sun.COM }
72611900SRaymond.Chen@Sun.COM
72711900SRaymond.Chen@Sun.COM usbecm_close_pipes(ecmp);
72811900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
72911900SRaymond.Chen@Sun.COM
73011900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
73111900SRaymond.Chen@Sun.COM ecmp->ecm_mac_state = USBECM_MAC_STOPPED;
73211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
73311900SRaymond.Chen@Sun.COM
73411900SRaymond.Chen@Sun.COM mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
73511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
73611900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
73711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
73811900SRaymond.Chen@Sun.COM
73911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
74011900SRaymond.Chen@Sun.COM "usbecm_m_stop: end");
74111900SRaymond.Chen@Sun.COM }
74211900SRaymond.Chen@Sun.COM
74311900SRaymond.Chen@Sun.COM /*
74411900SRaymond.Chen@Sun.COM * Change the MAC address of the device.
74511900SRaymond.Chen@Sun.COM */
74611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
74711900SRaymond.Chen@Sun.COM static int
usbecm_m_unicst(void * arg,const uint8_t * macaddr)74811900SRaymond.Chen@Sun.COM usbecm_m_unicst(void *arg, const uint8_t *macaddr)
74911900SRaymond.Chen@Sun.COM {
75011900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
75111900SRaymond.Chen@Sun.COM uint16_t filter;
75211900SRaymond.Chen@Sun.COM int rval;
75311900SRaymond.Chen@Sun.COM
75411900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
75511900SRaymond.Chen@Sun.COM "usbecm_m_unicst: entry");
75611900SRaymond.Chen@Sun.COM
75711900SRaymond.Chen@Sun.COM /*
75811900SRaymond.Chen@Sun.COM * The device doesn't support to set a different MAC addr.
75911900SRaymond.Chen@Sun.COM * Hence, it's not necessary to stop the device first if
76011900SRaymond.Chen@Sun.COM * the mac addresses are identical. And we just set unicast
76111900SRaymond.Chen@Sun.COM * filter only.
76211900SRaymond.Chen@Sun.COM */
76311900SRaymond.Chen@Sun.COM if (bcmp(macaddr, ecmp->ecm_srcaddr, ETHERADDRL) != 0) {
76411900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
76511900SRaymond.Chen@Sun.COM "usbecm_m_unicst: not supported to set a"
76611900SRaymond.Chen@Sun.COM " different MAC addr");
76711900SRaymond.Chen@Sun.COM
76811900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
76911900SRaymond.Chen@Sun.COM }
77011900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
77111900SRaymond.Chen@Sun.COM filter = ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_DIRECTED;
77211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
77311900SRaymond.Chen@Sun.COM
774*11902SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
77511900SRaymond.Chen@Sun.COM rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
77611900SRaymond.Chen@Sun.COM filter, NULL);
77711900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
77811900SRaymond.Chen@Sun.COM
77911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
78011900SRaymond.Chen@Sun.COM "usbecm_m_unicst: rval = %d", rval);
78111900SRaymond.Chen@Sun.COM
78211900SRaymond.Chen@Sun.COM /* some devices may not support this request, we just return success */
78311900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
78411900SRaymond.Chen@Sun.COM }
78511900SRaymond.Chen@Sun.COM
78611900SRaymond.Chen@Sun.COM /*
78711900SRaymond.Chen@Sun.COM * Enable/disable multicast.
78811900SRaymond.Chen@Sun.COM */
78911900SRaymond.Chen@Sun.COM /*ARGSUSED*/
79011900SRaymond.Chen@Sun.COM static int
usbecm_m_multicst(void * arg,boolean_t add,const uint8_t * m)79111900SRaymond.Chen@Sun.COM usbecm_m_multicst(void *arg, boolean_t add, const uint8_t *m)
79211900SRaymond.Chen@Sun.COM {
79311900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
79411900SRaymond.Chen@Sun.COM uint16_t filter;
79511900SRaymond.Chen@Sun.COM int rval = 0;
79611900SRaymond.Chen@Sun.COM
79711900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
79811900SRaymond.Chen@Sun.COM "usbecm_m_multicst: entry");
79911900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
80011900SRaymond.Chen@Sun.COM
80111900SRaymond.Chen@Sun.COM /*
80211900SRaymond.Chen@Sun.COM * To simplify the implementation, we support switching
80311900SRaymond.Chen@Sun.COM * all multicast on/off feature only
80411900SRaymond.Chen@Sun.COM */
80511900SRaymond.Chen@Sun.COM if (add == B_TRUE) {
80611900SRaymond.Chen@Sun.COM ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_ALL_MCAST;
80711900SRaymond.Chen@Sun.COM } else {
80811900SRaymond.Chen@Sun.COM ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_ALL_MCAST;
80911900SRaymond.Chen@Sun.COM }
81011900SRaymond.Chen@Sun.COM filter = ecmp->ecm_pkt_flt;
81111900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
81211900SRaymond.Chen@Sun.COM
813*11902SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
81411900SRaymond.Chen@Sun.COM if (ecmp->ecm_compatibility &&
81511900SRaymond.Chen@Sun.COM (ecmp->ecm_desc.wNumberMCFilters & 0x7F)) {
81611900SRaymond.Chen@Sun.COM /* Device supports SetEthernetMulticastFilters request */
81711900SRaymond.Chen@Sun.COM rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
81811900SRaymond.Chen@Sun.COM filter, NULL);
81911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
82011900SRaymond.Chen@Sun.COM "usbecm_m_multicst: rval = %d", rval);
82111900SRaymond.Chen@Sun.COM }
82211900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
82311900SRaymond.Chen@Sun.COM
82411900SRaymond.Chen@Sun.COM /* some devices may not support this request, we just return success */
82511900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
82611900SRaymond.Chen@Sun.COM }
82711900SRaymond.Chen@Sun.COM
82811900SRaymond.Chen@Sun.COM /*
82911900SRaymond.Chen@Sun.COM * Enable/disable promiscuous mode.
83011900SRaymond.Chen@Sun.COM */
83111900SRaymond.Chen@Sun.COM static int
usbecm_m_promisc(void * arg,boolean_t on)83211900SRaymond.Chen@Sun.COM usbecm_m_promisc(void *arg, boolean_t on)
83311900SRaymond.Chen@Sun.COM {
83411900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
83511900SRaymond.Chen@Sun.COM uint16_t filter;
83611900SRaymond.Chen@Sun.COM int rval;
83711900SRaymond.Chen@Sun.COM
83811900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
83911900SRaymond.Chen@Sun.COM "usbecm_m_promisc: entry");
84011900SRaymond.Chen@Sun.COM
84111900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
84211900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
84311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
84411900SRaymond.Chen@Sun.COM "usbecm_m_promisc: device not ONLINE");
84511900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
84611900SRaymond.Chen@Sun.COM
84711900SRaymond.Chen@Sun.COM return (DDI_FAILURE);
84811900SRaymond.Chen@Sun.COM }
84911900SRaymond.Chen@Sun.COM
85011900SRaymond.Chen@Sun.COM
85111900SRaymond.Chen@Sun.COM if (on == B_TRUE) {
85211900SRaymond.Chen@Sun.COM ecmp->ecm_pkt_flt |= CDC_ECM_PKT_TYPE_PROMISC;
85311900SRaymond.Chen@Sun.COM } else {
85411900SRaymond.Chen@Sun.COM ecmp->ecm_pkt_flt &= ~CDC_ECM_PKT_TYPE_PROMISC;
85511900SRaymond.Chen@Sun.COM }
85611900SRaymond.Chen@Sun.COM filter = ecmp->ecm_pkt_flt;
85711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
85811900SRaymond.Chen@Sun.COM
859*11902SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
86011900SRaymond.Chen@Sun.COM rval = usbecm_ctrl_write(ecmp, CDC_ECM_SET_ETH_PKT_FLT,
86111900SRaymond.Chen@Sun.COM filter, NULL);
86211900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
86311900SRaymond.Chen@Sun.COM
86411900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
86511900SRaymond.Chen@Sun.COM "usbecm_m_promisc: rval=%d", rval);
86611900SRaymond.Chen@Sun.COM
86711900SRaymond.Chen@Sun.COM /*
86811900SRaymond.Chen@Sun.COM * devices may not support this request, we just
86911900SRaymond.Chen@Sun.COM * return success to let upper layer to do further
87011900SRaymond.Chen@Sun.COM * operation.
87111900SRaymond.Chen@Sun.COM */
87211900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
87311900SRaymond.Chen@Sun.COM }
87411900SRaymond.Chen@Sun.COM
87511900SRaymond.Chen@Sun.COM /*
87611900SRaymond.Chen@Sun.COM * IOCTL request: Does not do anything. Will be enhanced
87711900SRaymond.Chen@Sun.COM * in future.
87811900SRaymond.Chen@Sun.COM */
87911900SRaymond.Chen@Sun.COM static void
usbecm_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)88011900SRaymond.Chen@Sun.COM usbecm_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
88111900SRaymond.Chen@Sun.COM {
88211900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
88311900SRaymond.Chen@Sun.COM struct iocblk *iocp;
88411900SRaymond.Chen@Sun.COM int cmd;
88511900SRaymond.Chen@Sun.COM
88611900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
88711900SRaymond.Chen@Sun.COM "usbecm_m_ioctl: entry");
88811900SRaymond.Chen@Sun.COM
88911900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
89011900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
89111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
89211900SRaymond.Chen@Sun.COM "usbecm_m_ioctl: device not ONLINE");
89311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
89411900SRaymond.Chen@Sun.COM
89511900SRaymond.Chen@Sun.COM miocnak(wq, mp, 0, EIO);
89611900SRaymond.Chen@Sun.COM
89711900SRaymond.Chen@Sun.COM return;
89811900SRaymond.Chen@Sun.COM }
89911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
90011900SRaymond.Chen@Sun.COM
90111900SRaymond.Chen@Sun.COM iocp = (void *)mp->b_rptr;
90211900SRaymond.Chen@Sun.COM iocp->ioc_error = 0;
90311900SRaymond.Chen@Sun.COM cmd = iocp->ioc_cmd;
90411900SRaymond.Chen@Sun.COM
905*11902SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
90611900SRaymond.Chen@Sun.COM
90711900SRaymond.Chen@Sun.COM switch (cmd) {
90811900SRaymond.Chen@Sun.COM default:
90911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
91011900SRaymond.Chen@Sun.COM "unknown cmd 0x%x", cmd);
91111900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
91211900SRaymond.Chen@Sun.COM miocnak(wq, mp, 0, EINVAL);
91311900SRaymond.Chen@Sun.COM
91411900SRaymond.Chen@Sun.COM return;
91511900SRaymond.Chen@Sun.COM }
91611900SRaymond.Chen@Sun.COM }
91711900SRaymond.Chen@Sun.COM
91811900SRaymond.Chen@Sun.COM /*
91911900SRaymond.Chen@Sun.COM * callback functions for get/set properties
92011900SRaymond.Chen@Sun.COM * Does not do anything. Will be enhanced to
92111900SRaymond.Chen@Sun.COM * support set/get properties in future.
92211900SRaymond.Chen@Sun.COM */
92311900SRaymond.Chen@Sun.COM /*ARGSUSED*/
92411900SRaymond.Chen@Sun.COM 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)92511900SRaymond.Chen@Sun.COM usbecm_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
92611900SRaymond.Chen@Sun.COM uint_t wldp_length, const void *wldp_buf)
92711900SRaymond.Chen@Sun.COM {
92811900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
92911900SRaymond.Chen@Sun.COM int err = ENOTSUP;
93011900SRaymond.Chen@Sun.COM
93111900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
93211900SRaymond.Chen@Sun.COM "usbecm_m_setprop: entry");
93311900SRaymond.Chen@Sun.COM
93411900SRaymond.Chen@Sun.COM return (err);
93511900SRaymond.Chen@Sun.COM }
93611900SRaymond.Chen@Sun.COM
93711900SRaymond.Chen@Sun.COM /*ARGSUSED*/
usbecm_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)93811900SRaymond.Chen@Sun.COM static int usbecm_m_getprop(void *arg, const char *pr_name,
93911900SRaymond.Chen@Sun.COM mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf)
94011900SRaymond.Chen@Sun.COM {
94111900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
94211900SRaymond.Chen@Sun.COM int err = ENOTSUP;
94311900SRaymond.Chen@Sun.COM
94411900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
94511900SRaymond.Chen@Sun.COM "usbecm_m_getprop: entry");
94611900SRaymond.Chen@Sun.COM
94711900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
94811900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
94911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
95011900SRaymond.Chen@Sun.COM
95111900SRaymond.Chen@Sun.COM return (EIO);
95211900SRaymond.Chen@Sun.COM }
95311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
95411900SRaymond.Chen@Sun.COM
95511900SRaymond.Chen@Sun.COM return (err);
95611900SRaymond.Chen@Sun.COM }
95711900SRaymond.Chen@Sun.COM
95811900SRaymond.Chen@Sun.COM /*
95911900SRaymond.Chen@Sun.COM * Transmit a data frame.
96011900SRaymond.Chen@Sun.COM */
96111900SRaymond.Chen@Sun.COM static mblk_t *
usbecm_m_tx(void * arg,mblk_t * mp)96211900SRaymond.Chen@Sun.COM usbecm_m_tx(void *arg, mblk_t *mp)
96311900SRaymond.Chen@Sun.COM {
96411900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)arg;
96511900SRaymond.Chen@Sun.COM mblk_t *next;
96611900SRaymond.Chen@Sun.COM int count = 0;
96711900SRaymond.Chen@Sun.COM
96811900SRaymond.Chen@Sun.COM ASSERT(mp != NULL);
96911900SRaymond.Chen@Sun.COM
97011900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
97111900SRaymond.Chen@Sun.COM "usbecm_m_tx: entry");
97211900SRaymond.Chen@Sun.COM
97311900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
97411900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
97511900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
97611900SRaymond.Chen@Sun.COM "usbecm_m_tx: device not ONLINE");
97711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
97811900SRaymond.Chen@Sun.COM
97911900SRaymond.Chen@Sun.COM return (mp);
98011900SRaymond.Chen@Sun.COM }
98111900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
98211900SRaymond.Chen@Sun.COM
983*11902SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
98411900SRaymond.Chen@Sun.COM
98511900SRaymond.Chen@Sun.COM /*
98611900SRaymond.Chen@Sun.COM * To make use of the device maximum capability,
98711900SRaymond.Chen@Sun.COM * concatenate msg blocks in a msg to ETHERMAX length.
98811900SRaymond.Chen@Sun.COM */
98911900SRaymond.Chen@Sun.COM while (mp != NULL) {
99011900SRaymond.Chen@Sun.COM next = mp->b_next;
99111900SRaymond.Chen@Sun.COM mp->b_next = NULL;
99211900SRaymond.Chen@Sun.COM
99311900SRaymond.Chen@Sun.COM if (usbecm_send_data(ecmp, mp) != DDI_SUCCESS) {
99411900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_OPS, ecmp->ecm_lh,
99511900SRaymond.Chen@Sun.COM "usbecm_m_tx: send data fail");
99611900SRaymond.Chen@Sun.COM
99711900SRaymond.Chen@Sun.COM /* failure statistics */
99811900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
99911900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_oerrors++;
100011900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
100111900SRaymond.Chen@Sun.COM
100211900SRaymond.Chen@Sun.COM mp->b_next = next;
100311900SRaymond.Chen@Sun.COM
100411900SRaymond.Chen@Sun.COM break;
100511900SRaymond.Chen@Sun.COM }
100611900SRaymond.Chen@Sun.COM
100711900SRaymond.Chen@Sun.COM /*
100811900SRaymond.Chen@Sun.COM * To make it simple, we count all packets, no matter
100911900SRaymond.Chen@Sun.COM * the device supports ethernet statistics or not.
101011900SRaymond.Chen@Sun.COM */
101111900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
101211900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_opackets++;
101311900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_obytes += MBLKL(mp);
101411900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
101511900SRaymond.Chen@Sun.COM
101611900SRaymond.Chen@Sun.COM freemsg(mp); /* free this msg upon success */
101711900SRaymond.Chen@Sun.COM
101811900SRaymond.Chen@Sun.COM mp = next;
101911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
102011900SRaymond.Chen@Sun.COM "usbecm_m_tx: %d msgs processed", ++count);
102111900SRaymond.Chen@Sun.COM }
102211900SRaymond.Chen@Sun.COM
102311900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
102411900SRaymond.Chen@Sun.COM
102511900SRaymond.Chen@Sun.COM return (mp);
102611900SRaymond.Chen@Sun.COM }
102711900SRaymond.Chen@Sun.COM
102811900SRaymond.Chen@Sun.COM /*
102911900SRaymond.Chen@Sun.COM * usbecm_bulkin_cb:
103011900SRaymond.Chen@Sun.COM * Bulk In regular and exeception callback;
103111900SRaymond.Chen@Sun.COM * USBA framework will call this callback
103211900SRaymond.Chen@Sun.COM * after deal with bulkin request.
103311900SRaymond.Chen@Sun.COM */
103411900SRaymond.Chen@Sun.COM /*ARGSUSED*/
103511900SRaymond.Chen@Sun.COM static void
usbecm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)103611900SRaymond.Chen@Sun.COM usbecm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
103711900SRaymond.Chen@Sun.COM {
103811900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private;
103911900SRaymond.Chen@Sun.COM mblk_t *data, *mp;
104011900SRaymond.Chen@Sun.COM int data_len;
104111900SRaymond.Chen@Sun.COM int max_pkt_size = ecmp->ecm_bulkin_sz;
104211900SRaymond.Chen@Sun.COM
104311900SRaymond.Chen@Sun.COM data = req->bulk_data;
104411900SRaymond.Chen@Sun.COM data_len = (data) ? MBLKL(data) : 0;
104511900SRaymond.Chen@Sun.COM
104611900SRaymond.Chen@Sun.COM ASSERT(data->b_cont == NULL);
104711900SRaymond.Chen@Sun.COM
104811900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
104911900SRaymond.Chen@Sun.COM
105011900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
105111900SRaymond.Chen@Sun.COM "usbecm_bulkin_cb: state=%d, len=%d", ecmp->ecm_bulkin_state,
105211900SRaymond.Chen@Sun.COM data_len);
105311900SRaymond.Chen@Sun.COM
105411900SRaymond.Chen@Sun.COM /*
105511900SRaymond.Chen@Sun.COM * may receive a zero length packet according
105611900SRaymond.Chen@Sun.COM * to USB short packet semantics
105711900SRaymond.Chen@Sun.COM */
105811900SRaymond.Chen@Sun.COM if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
105911900SRaymond.Chen@Sun.COM (req->bulk_completion_reason == USB_CR_OK)) {
106011900SRaymond.Chen@Sun.COM if (data_len) {
106111900SRaymond.Chen@Sun.COM if (ecmp->ecm_rcv_queue == NULL) {
106211900SRaymond.Chen@Sun.COM ecmp->ecm_rcv_queue = data;
106311900SRaymond.Chen@Sun.COM } else {
106411900SRaymond.Chen@Sun.COM if ((msgsize(ecmp->ecm_rcv_queue) + data_len)
106511900SRaymond.Chen@Sun.COM > ETHERMAX) {
106611900SRaymond.Chen@Sun.COM /*
106711900SRaymond.Chen@Sun.COM * Exceed the ethernet maximum length, we think
106811900SRaymond.Chen@Sun.COM * something is wrong with this frame and hence
106911900SRaymond.Chen@Sun.COM * free older data. Accept new data instead.
107011900SRaymond.Chen@Sun.COM */
107111900SRaymond.Chen@Sun.COM freemsg(ecmp->ecm_rcv_queue);
107211900SRaymond.Chen@Sun.COM ecmp->ecm_rcv_queue = data;
107311900SRaymond.Chen@Sun.COM } else {
107411900SRaymond.Chen@Sun.COM linkb(ecmp->ecm_rcv_queue, data);
107511900SRaymond.Chen@Sun.COM }
107611900SRaymond.Chen@Sun.COM }
107711900SRaymond.Chen@Sun.COM } else {
107811900SRaymond.Chen@Sun.COM /*
107911900SRaymond.Chen@Sun.COM * Do not put zero length packet to receive queue.
108011900SRaymond.Chen@Sun.COM * Otherwise, msgpullup will dupmsg() a zero length
108111900SRaymond.Chen@Sun.COM * mblk, which will cause memleaks.
108211900SRaymond.Chen@Sun.COM */
108311900SRaymond.Chen@Sun.COM freemsg(data);
108411900SRaymond.Chen@Sun.COM }
108511900SRaymond.Chen@Sun.COM
108611900SRaymond.Chen@Sun.COM /*
108711900SRaymond.Chen@Sun.COM * ECM V1.2, section 3.3.1, a short(including zero length)
108811900SRaymond.Chen@Sun.COM * packet signifies end of frame. We can submit this frame
108911900SRaymond.Chen@Sun.COM * to upper layer now.
109011900SRaymond.Chen@Sun.COM */
109111900SRaymond.Chen@Sun.COM if ((data_len < max_pkt_size) &&
109211900SRaymond.Chen@Sun.COM (msgsize(ecmp->ecm_rcv_queue) > 0)) {
109311900SRaymond.Chen@Sun.COM mp = msgpullup(ecmp->ecm_rcv_queue, -1);
109411900SRaymond.Chen@Sun.COM freemsg(ecmp->ecm_rcv_queue);
109511900SRaymond.Chen@Sun.COM ecmp->ecm_rcv_queue = NULL;
109611900SRaymond.Chen@Sun.COM
109711900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_ipackets++;
109811900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_ibytes += msgsize(mp);
109911900SRaymond.Chen@Sun.COM if (mp && (mp->b_rptr[0] & 0x01)) {
110011900SRaymond.Chen@Sun.COM if (bcmp(mp->b_rptr, usbecm_broadcast,
110111900SRaymond.Chen@Sun.COM ETHERADDRL) != 0) {
110211900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_multircv++;
110311900SRaymond.Chen@Sun.COM } else {
110411900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_brdcstrcv++;
110511900SRaymond.Chen@Sun.COM }
110611900SRaymond.Chen@Sun.COM }
110711900SRaymond.Chen@Sun.COM
110811900SRaymond.Chen@Sun.COM if (mp) {
110911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
111011900SRaymond.Chen@Sun.COM mac_rx(ecmp->ecm_mh, NULL, mp);
111111900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
111211900SRaymond.Chen@Sun.COM }
111311900SRaymond.Chen@Sun.COM }
111411900SRaymond.Chen@Sun.COM
111511900SRaymond.Chen@Sun.COM /* prevent USBA from freeing data along with the request */
111611900SRaymond.Chen@Sun.COM req->bulk_data = NULL;
111711900SRaymond.Chen@Sun.COM } else if (req->bulk_completion_reason != USB_CR_OK) {
111811900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_ierrors++;
111911900SRaymond.Chen@Sun.COM }
112011900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
112111900SRaymond.Chen@Sun.COM
112211900SRaymond.Chen@Sun.COM usb_free_bulk_req(req);
112311900SRaymond.Chen@Sun.COM
112411900SRaymond.Chen@Sun.COM /* receive more */
112511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
112611900SRaymond.Chen@Sun.COM if (((ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) ||
112711900SRaymond.Chen@Sun.COM (ecmp->ecm_bulkin_state == USBECM_PIPE_IDLE)) &&
112811900SRaymond.Chen@Sun.COM (ecmp->ecm_dev_state == USB_DEV_ONLINE)) {
112911900SRaymond.Chen@Sun.COM if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
113011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
113111900SRaymond.Chen@Sun.COM "usbecm_bulkin_cb: restart rx fail "
113211900SRaymond.Chen@Sun.COM "ecmp_state = %d", ecmp->ecm_bulkin_state);
113311900SRaymond.Chen@Sun.COM }
113411900SRaymond.Chen@Sun.COM } else if (ecmp->ecm_bulkin_state == USBECM_PIPE_BUSY) {
113511900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
113611900SRaymond.Chen@Sun.COM }
113711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
113811900SRaymond.Chen@Sun.COM }
113911900SRaymond.Chen@Sun.COM
114011900SRaymond.Chen@Sun.COM /*
114111900SRaymond.Chen@Sun.COM * usbsecm_rx_start:
114211900SRaymond.Chen@Sun.COM * start data receipt
114311900SRaymond.Chen@Sun.COM */
114411900SRaymond.Chen@Sun.COM static int
usbecm_rx_start(usbecm_state_t * ecmp)114511900SRaymond.Chen@Sun.COM usbecm_rx_start(usbecm_state_t *ecmp)
114611900SRaymond.Chen@Sun.COM {
114711900SRaymond.Chen@Sun.COM usb_bulk_req_t *br;
114811900SRaymond.Chen@Sun.COM int rval = USB_FAILURE;
114911900SRaymond.Chen@Sun.COM int data_len;
115011900SRaymond.Chen@Sun.COM
115111900SRaymond.Chen@Sun.COM ASSERT(mutex_owned(&ecmp->ecm_mutex));
115211900SRaymond.Chen@Sun.COM
115311900SRaymond.Chen@Sun.COM DTRACE_PROBE2(usbecm_rx__start, int, ecmp->ecm_xfer_sz,
115411900SRaymond.Chen@Sun.COM int, ecmp->ecm_bulkin_sz);
115511900SRaymond.Chen@Sun.COM
115611900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_state = USBECM_PIPE_BUSY;
115711900SRaymond.Chen@Sun.COM data_len = ecmp->ecm_bulkin_sz;
115811900SRaymond.Chen@Sun.COM
115911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
116011900SRaymond.Chen@Sun.COM br = usb_alloc_bulk_req(ecmp->ecm_dip, data_len, USB_FLAGS_SLEEP);
116111900SRaymond.Chen@Sun.COM if (br == NULL) {
116211900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
116311900SRaymond.Chen@Sun.COM "usbsecm_rx_start: allocate bulk request failed");
116411900SRaymond.Chen@Sun.COM
116511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
116611900SRaymond.Chen@Sun.COM
116711900SRaymond.Chen@Sun.COM return (USB_FAILURE);
116811900SRaymond.Chen@Sun.COM }
116911900SRaymond.Chen@Sun.COM /* initialize bulk in request. */
117011900SRaymond.Chen@Sun.COM br->bulk_len = data_len;
117111900SRaymond.Chen@Sun.COM br->bulk_timeout = 0;
117211900SRaymond.Chen@Sun.COM br->bulk_cb = usbecm_bulkin_cb;
117311900SRaymond.Chen@Sun.COM br->bulk_exc_cb = usbecm_bulkin_cb;
117411900SRaymond.Chen@Sun.COM br->bulk_client_private = (usb_opaque_t)ecmp;
117511900SRaymond.Chen@Sun.COM br->bulk_attributes = USB_ATTRS_AUTOCLEARING
117611900SRaymond.Chen@Sun.COM | USB_ATTRS_SHORT_XFER_OK;
117711900SRaymond.Chen@Sun.COM
117811900SRaymond.Chen@Sun.COM rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkin_ph, br, 0);
117911900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
118011900SRaymond.Chen@Sun.COM if (rval != USB_SUCCESS) {
118111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
118211900SRaymond.Chen@Sun.COM "usbsecm_rx_start: bulk transfer failed %d", rval);
118311900SRaymond.Chen@Sun.COM usb_free_bulk_req(br);
118411900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
118511900SRaymond.Chen@Sun.COM }
118611900SRaymond.Chen@Sun.COM
118711900SRaymond.Chen@Sun.COM return (rval);
118811900SRaymond.Chen@Sun.COM }
118911900SRaymond.Chen@Sun.COM
119011900SRaymond.Chen@Sun.COM /*
119111900SRaymond.Chen@Sun.COM * usbecm_bulkout_cb:
119211900SRaymond.Chen@Sun.COM * Bulk Out regular and exeception callback;
119311900SRaymond.Chen@Sun.COM * USBA framework will call this callback function
119411900SRaymond.Chen@Sun.COM * after deal with bulkout request.
119511900SRaymond.Chen@Sun.COM */
119611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
119711900SRaymond.Chen@Sun.COM static void
usbecm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)119811900SRaymond.Chen@Sun.COM usbecm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
119911900SRaymond.Chen@Sun.COM {
120011900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)req->bulk_client_private;
120111900SRaymond.Chen@Sun.COM int data_len;
120211900SRaymond.Chen@Sun.COM boolean_t need_update = B_FALSE;
120311900SRaymond.Chen@Sun.COM
120411900SRaymond.Chen@Sun.COM data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
120511900SRaymond.Chen@Sun.COM
120611900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_CB, ecmp->ecm_lh,
120711900SRaymond.Chen@Sun.COM "usbecm_bulkout_cb: data_len = %d, cr=%d", data_len,
120811900SRaymond.Chen@Sun.COM req->bulk_completion_reason);
120911900SRaymond.Chen@Sun.COM
121011900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
121111900SRaymond.Chen@Sun.COM if ((data_len > 0) && (ecmp->ecm_tx_cnt > 0)) {
121211900SRaymond.Chen@Sun.COM if (ecmp->ecm_tx_cnt == usbecm_tx_max) {
121311900SRaymond.Chen@Sun.COM need_update = B_TRUE;
121411900SRaymond.Chen@Sun.COM }
121511900SRaymond.Chen@Sun.COM ecmp->ecm_tx_cnt--;
121611900SRaymond.Chen@Sun.COM }
121711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
121811900SRaymond.Chen@Sun.COM
121911900SRaymond.Chen@Sun.COM if (req->bulk_completion_reason && (data_len > 0)) {
122011900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
122111900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_oerrors++;
122211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
122311900SRaymond.Chen@Sun.COM
122411900SRaymond.Chen@Sun.COM need_update = B_TRUE;
122511900SRaymond.Chen@Sun.COM }
122611900SRaymond.Chen@Sun.COM
122711900SRaymond.Chen@Sun.COM /*
122811900SRaymond.Chen@Sun.COM * notify MAC layer to retransfer the failed packet
122911900SRaymond.Chen@Sun.COM * Or notity MAC that we have more buffer now.
123011900SRaymond.Chen@Sun.COM */
123111900SRaymond.Chen@Sun.COM if (need_update) {
123211900SRaymond.Chen@Sun.COM mac_tx_update(ecmp->ecm_mh);
123311900SRaymond.Chen@Sun.COM }
123411900SRaymond.Chen@Sun.COM
123511900SRaymond.Chen@Sun.COM usb_free_bulk_req(req);
123611900SRaymond.Chen@Sun.COM }
123711900SRaymond.Chen@Sun.COM
123811900SRaymond.Chen@Sun.COM static int
usbecm_send_data(usbecm_state_t * ecmp,mblk_t * data)123911900SRaymond.Chen@Sun.COM usbecm_send_data(usbecm_state_t *ecmp, mblk_t *data)
124011900SRaymond.Chen@Sun.COM {
124111900SRaymond.Chen@Sun.COM usb_bulk_req_t *br;
124211900SRaymond.Chen@Sun.COM int rval = USB_FAILURE;
124311900SRaymond.Chen@Sun.COM int data_len = MBLKL(data);
124411900SRaymond.Chen@Sun.COM int max_pkt_size;
124511900SRaymond.Chen@Sun.COM mblk_t *new_data = NULL;
124611900SRaymond.Chen@Sun.COM int new_data_len = 0;
124711900SRaymond.Chen@Sun.COM
124811900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
124911900SRaymond.Chen@Sun.COM "usbecm_send_data: length = %d, total len=%d",
125011900SRaymond.Chen@Sun.COM data_len, (int)msgdsize(data));
125111900SRaymond.Chen@Sun.COM
125211900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
125311900SRaymond.Chen@Sun.COM if (ecmp->ecm_tx_cnt >= usbecm_tx_max) {
125411900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
125511900SRaymond.Chen@Sun.COM "usbecm_send_data: (%d) exceeds TX max queue length",
125611900SRaymond.Chen@Sun.COM ecmp->ecm_tx_cnt);
125711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
125811900SRaymond.Chen@Sun.COM
125911900SRaymond.Chen@Sun.COM return (USB_FAILURE);
126011900SRaymond.Chen@Sun.COM }
126111900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
126211900SRaymond.Chen@Sun.COM
126311900SRaymond.Chen@Sun.COM data_len = msgsize(data);
126411900SRaymond.Chen@Sun.COM if (data_len > ETHERMAX) {
126511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
126611900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_macxmt_err++;
126711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
126811900SRaymond.Chen@Sun.COM
126911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
127011900SRaymond.Chen@Sun.COM "usbecm_send_data: packet too long, %d", data_len);
127111900SRaymond.Chen@Sun.COM
127211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
127311900SRaymond.Chen@Sun.COM }
127411900SRaymond.Chen@Sun.COM
127511900SRaymond.Chen@Sun.COM if (data_len < ETHERMIN) {
127611900SRaymond.Chen@Sun.COM mblk_t *tmp;
127711900SRaymond.Chen@Sun.COM
127811900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
127911900SRaymond.Chen@Sun.COM "usbecm_send_data: short packet, padding to ETHERMIN");
128011900SRaymond.Chen@Sun.COM
128111900SRaymond.Chen@Sun.COM new_data_len = ETHERMIN;
128211900SRaymond.Chen@Sun.COM if ((new_data = allocb(new_data_len, 0)) == NULL) {
128311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
128411900SRaymond.Chen@Sun.COM "usbecm_send_data: fail to allocb");
128511900SRaymond.Chen@Sun.COM
128611900SRaymond.Chen@Sun.COM return (USB_FAILURE);
128711900SRaymond.Chen@Sun.COM }
128811900SRaymond.Chen@Sun.COM bzero(new_data->b_wptr, new_data_len);
128911900SRaymond.Chen@Sun.COM for (tmp = data; tmp != NULL; tmp = tmp->b_cont) {
129011900SRaymond.Chen@Sun.COM bcopy(tmp->b_rptr, new_data->b_wptr, MBLKL(tmp));
129111900SRaymond.Chen@Sun.COM new_data->b_wptr += MBLKL(tmp);
129211900SRaymond.Chen@Sun.COM }
129311900SRaymond.Chen@Sun.COM
129411900SRaymond.Chen@Sun.COM new_data->b_wptr = new_data->b_rptr + new_data_len;
129511900SRaymond.Chen@Sun.COM }
129611900SRaymond.Chen@Sun.COM
129711900SRaymond.Chen@Sun.COM br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
129811900SRaymond.Chen@Sun.COM if (br == NULL) {
129911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
130011900SRaymond.Chen@Sun.COM "usbecm_send_data: alloc req failed.");
130111900SRaymond.Chen@Sun.COM
130211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
130311900SRaymond.Chen@Sun.COM }
130411900SRaymond.Chen@Sun.COM
130511900SRaymond.Chen@Sun.COM /* initialize the bulk out request */
130611900SRaymond.Chen@Sun.COM if (new_data) {
130711900SRaymond.Chen@Sun.COM br->bulk_data = msgpullup(new_data, -1); /* msg allocated! */
130811900SRaymond.Chen@Sun.COM br->bulk_len = new_data_len;
130911900SRaymond.Chen@Sun.COM } else {
131011900SRaymond.Chen@Sun.COM br->bulk_data = msgpullup(data, -1); /* msg allocated! */
131111900SRaymond.Chen@Sun.COM br->bulk_len = data_len;
131211900SRaymond.Chen@Sun.COM }
131311900SRaymond.Chen@Sun.COM
131411900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
131511900SRaymond.Chen@Sun.COM "usbecm_send_data: bulk_len = %d", br->bulk_len);
131611900SRaymond.Chen@Sun.COM
131711900SRaymond.Chen@Sun.COM br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
131811900SRaymond.Chen@Sun.COM br->bulk_cb = usbecm_bulkout_cb;
131911900SRaymond.Chen@Sun.COM br->bulk_exc_cb = usbecm_bulkout_cb;
132011900SRaymond.Chen@Sun.COM br->bulk_client_private = (usb_opaque_t)ecmp;
132111900SRaymond.Chen@Sun.COM br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
132211900SRaymond.Chen@Sun.COM
132311900SRaymond.Chen@Sun.COM if (br->bulk_data != NULL) {
132411900SRaymond.Chen@Sun.COM if (br->bulk_data->b_rptr[0] & 0x01) {
132511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
132611900SRaymond.Chen@Sun.COM if (bcmp(br->bulk_data->b_rptr, usbecm_broadcast,
132711900SRaymond.Chen@Sun.COM ETHERADDRL) != 0) {
132811900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_multixmt++;
132911900SRaymond.Chen@Sun.COM } else {
133011900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_brdcstxmt++;
133111900SRaymond.Chen@Sun.COM }
133211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
133311900SRaymond.Chen@Sun.COM }
133411900SRaymond.Chen@Sun.COM rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
133511900SRaymond.Chen@Sun.COM }
133611900SRaymond.Chen@Sun.COM
133711900SRaymond.Chen@Sun.COM if (rval != USB_SUCCESS) {
133811900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
133911900SRaymond.Chen@Sun.COM "usbecm_send_data: Send Data failed.");
134011900SRaymond.Chen@Sun.COM
134111900SRaymond.Chen@Sun.COM /*
134211900SRaymond.Chen@Sun.COM * br->bulk_data should be freed because we allocated
134311900SRaymond.Chen@Sun.COM * it in this function.
134411900SRaymond.Chen@Sun.COM */
134511900SRaymond.Chen@Sun.COM usb_free_bulk_req(br);
134611900SRaymond.Chen@Sun.COM
134711900SRaymond.Chen@Sun.COM } else {
134811900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
134911900SRaymond.Chen@Sun.COM ecmp->ecm_tx_cnt++;
135011900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
135111900SRaymond.Chen@Sun.COM
135211900SRaymond.Chen@Sun.COM /*
135311900SRaymond.Chen@Sun.COM * ECM V1.2, section 3.3.1, a short(including zero length)
135411900SRaymond.Chen@Sun.COM * packet signifies end of frame. We should send a zero length
135511900SRaymond.Chen@Sun.COM * packet to device if the total data lenght is multiple of
135611900SRaymond.Chen@Sun.COM * bulkout endpoint's max packet size.
135711900SRaymond.Chen@Sun.COM */
135811900SRaymond.Chen@Sun.COM max_pkt_size = ecmp->ecm_bulk_out_ep->ep_descr.wMaxPacketSize;
135911900SRaymond.Chen@Sun.COM if ((data_len % max_pkt_size) == 0) {
136011900SRaymond.Chen@Sun.COM if ((rval = usbecm_send_zero_data(ecmp))
136111900SRaymond.Chen@Sun.COM != USB_SUCCESS) {
136211900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
136311900SRaymond.Chen@Sun.COM "usbecm_send_data: fail to send padding");
136411900SRaymond.Chen@Sun.COM }
136511900SRaymond.Chen@Sun.COM }
136611900SRaymond.Chen@Sun.COM }
136711900SRaymond.Chen@Sun.COM
136811900SRaymond.Chen@Sun.COM if (new_data) {
136911900SRaymond.Chen@Sun.COM freemsg(new_data);
137011900SRaymond.Chen@Sun.COM }
137111900SRaymond.Chen@Sun.COM
137211900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
137311900SRaymond.Chen@Sun.COM "usbecm_send_data: len(%d) data sent, rval=%d",
137411900SRaymond.Chen@Sun.COM new_data_len ? new_data_len : data_len, rval);
137511900SRaymond.Chen@Sun.COM
137611900SRaymond.Chen@Sun.COM return (rval);
137711900SRaymond.Chen@Sun.COM }
137811900SRaymond.Chen@Sun.COM
137911900SRaymond.Chen@Sun.COM static int
usbecm_send_zero_data(usbecm_state_t * ecmp)138011900SRaymond.Chen@Sun.COM usbecm_send_zero_data(usbecm_state_t *ecmp)
138111900SRaymond.Chen@Sun.COM {
138211900SRaymond.Chen@Sun.COM usb_bulk_req_t *br;
138311900SRaymond.Chen@Sun.COM int rval = USB_FAILURE;
138411900SRaymond.Chen@Sun.COM
138511900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
138611900SRaymond.Chen@Sun.COM "usbecm_send_zero_data: entry");
138711900SRaymond.Chen@Sun.COM
138811900SRaymond.Chen@Sun.COM br = usb_alloc_bulk_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
138911900SRaymond.Chen@Sun.COM if (br == NULL) {
139011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
139111900SRaymond.Chen@Sun.COM "usbecm_send_data: alloc req failed.");
139211900SRaymond.Chen@Sun.COM
139311900SRaymond.Chen@Sun.COM return (USB_FAILURE);
139411900SRaymond.Chen@Sun.COM }
139511900SRaymond.Chen@Sun.COM
139611900SRaymond.Chen@Sun.COM /* initialize the bulk out request */
139711900SRaymond.Chen@Sun.COM br->bulk_len = 0;
139811900SRaymond.Chen@Sun.COM br->bulk_timeout = USBECM_BULKOUT_TIMEOUT;
139911900SRaymond.Chen@Sun.COM br->bulk_cb = usbecm_bulkout_cb;
140011900SRaymond.Chen@Sun.COM br->bulk_exc_cb = usbecm_bulkout_cb;
140111900SRaymond.Chen@Sun.COM br->bulk_client_private = (usb_opaque_t)ecmp;
140211900SRaymond.Chen@Sun.COM br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
140311900SRaymond.Chen@Sun.COM
140411900SRaymond.Chen@Sun.COM rval = usb_pipe_bulk_xfer(ecmp->ecm_bulkout_ph, br, 0);
140511900SRaymond.Chen@Sun.COM
140611900SRaymond.Chen@Sun.COM if (rval != USB_SUCCESS) {
140711900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPS, ecmp->ecm_lh,
140811900SRaymond.Chen@Sun.COM "usbecm_send_zero_data: Send data failed, rval=%d",
140911900SRaymond.Chen@Sun.COM rval);
141011900SRaymond.Chen@Sun.COM
141111900SRaymond.Chen@Sun.COM /*
141211900SRaymond.Chen@Sun.COM * br->bulk_data should be freed because we allocated
141311900SRaymond.Chen@Sun.COM * it in this function.
141411900SRaymond.Chen@Sun.COM */
141511900SRaymond.Chen@Sun.COM usb_free_bulk_req(br);
141611900SRaymond.Chen@Sun.COM
141711900SRaymond.Chen@Sun.COM }
141811900SRaymond.Chen@Sun.COM
141911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPS, ecmp->ecm_lh,
142011900SRaymond.Chen@Sun.COM "usbecm_send_zero_data: end");
142111900SRaymond.Chen@Sun.COM
142211900SRaymond.Chen@Sun.COM return (rval);
142311900SRaymond.Chen@Sun.COM }
142411900SRaymond.Chen@Sun.COM
142511900SRaymond.Chen@Sun.COM /*
142611900SRaymond.Chen@Sun.COM * Loadable module configuration entry points
142711900SRaymond.Chen@Sun.COM */
142811900SRaymond.Chen@Sun.COM
142911900SRaymond.Chen@Sun.COM /*
143011900SRaymond.Chen@Sun.COM * _init module entry point.
143111900SRaymond.Chen@Sun.COM *
143211900SRaymond.Chen@Sun.COM * Called when the module is being loaded into memory.
143311900SRaymond.Chen@Sun.COM */
143411900SRaymond.Chen@Sun.COM int
_init(void)143511900SRaymond.Chen@Sun.COM _init(void)
143611900SRaymond.Chen@Sun.COM {
143711900SRaymond.Chen@Sun.COM int err;
143811900SRaymond.Chen@Sun.COM
143911900SRaymond.Chen@Sun.COM err = ddi_soft_state_init(&usbecm_statep, sizeof (usbecm_state_t), 1);
144011900SRaymond.Chen@Sun.COM
144111900SRaymond.Chen@Sun.COM if (err != DDI_SUCCESS)
144211900SRaymond.Chen@Sun.COM return (err);
144311900SRaymond.Chen@Sun.COM
144411900SRaymond.Chen@Sun.COM mac_init_ops(&usbecm_devops, "usbecm");
144511900SRaymond.Chen@Sun.COM err = mod_install(&usbecm_ml);
144611900SRaymond.Chen@Sun.COM
144711900SRaymond.Chen@Sun.COM if (err != DDI_SUCCESS) {
144811900SRaymond.Chen@Sun.COM mac_fini_ops(&usbecm_devops);
144911900SRaymond.Chen@Sun.COM ddi_soft_state_fini(&usbecm_statep);
145011900SRaymond.Chen@Sun.COM }
145111900SRaymond.Chen@Sun.COM
145211900SRaymond.Chen@Sun.COM return (err);
145311900SRaymond.Chen@Sun.COM }
145411900SRaymond.Chen@Sun.COM
145511900SRaymond.Chen@Sun.COM /*
145611900SRaymond.Chen@Sun.COM * _info module entry point.
145711900SRaymond.Chen@Sun.COM *
145811900SRaymond.Chen@Sun.COM * Called to obtain information about the module.
145911900SRaymond.Chen@Sun.COM */
146011900SRaymond.Chen@Sun.COM int
_info(struct modinfo * modinfop)146111900SRaymond.Chen@Sun.COM _info(struct modinfo *modinfop)
146211900SRaymond.Chen@Sun.COM {
146311900SRaymond.Chen@Sun.COM return (mod_info(&usbecm_ml, modinfop));
146411900SRaymond.Chen@Sun.COM }
146511900SRaymond.Chen@Sun.COM
146611900SRaymond.Chen@Sun.COM /*
146711900SRaymond.Chen@Sun.COM * _fini module entry point.
146811900SRaymond.Chen@Sun.COM *
146911900SRaymond.Chen@Sun.COM * Called when the module is being unloaded.
147011900SRaymond.Chen@Sun.COM */
147111900SRaymond.Chen@Sun.COM int
_fini(void)147211900SRaymond.Chen@Sun.COM _fini(void)
147311900SRaymond.Chen@Sun.COM {
147411900SRaymond.Chen@Sun.COM int err;
147511900SRaymond.Chen@Sun.COM
147611900SRaymond.Chen@Sun.COM err = mod_remove(&usbecm_ml);
147711900SRaymond.Chen@Sun.COM if (err == DDI_SUCCESS) {
147811900SRaymond.Chen@Sun.COM mac_fini_ops(&usbecm_devops);
147911900SRaymond.Chen@Sun.COM ddi_soft_state_fini(&usbecm_statep);
148011900SRaymond.Chen@Sun.COM }
148111900SRaymond.Chen@Sun.COM
148211900SRaymond.Chen@Sun.COM return (err);
148311900SRaymond.Chen@Sun.COM }
148411900SRaymond.Chen@Sun.COM
148511900SRaymond.Chen@Sun.COM /*
148611900SRaymond.Chen@Sun.COM * usbecm_pipe_start_polling:
148711900SRaymond.Chen@Sun.COM * start polling on the interrupt pipe
148811900SRaymond.Chen@Sun.COM */
148911900SRaymond.Chen@Sun.COM static void
usbecm_pipe_start_polling(usbecm_state_t * ecmp)149011900SRaymond.Chen@Sun.COM usbecm_pipe_start_polling(usbecm_state_t *ecmp)
149111900SRaymond.Chen@Sun.COM {
149211900SRaymond.Chen@Sun.COM usb_intr_req_t *intr;
149311900SRaymond.Chen@Sun.COM int rval;
149411900SRaymond.Chen@Sun.COM
149511900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
149611900SRaymond.Chen@Sun.COM "usbecm_pipe_start_polling: ");
149711900SRaymond.Chen@Sun.COM
149811900SRaymond.Chen@Sun.COM if (ecmp->ecm_intr_ph == NULL) {
149911900SRaymond.Chen@Sun.COM
150011900SRaymond.Chen@Sun.COM return;
150111900SRaymond.Chen@Sun.COM }
150211900SRaymond.Chen@Sun.COM
150311900SRaymond.Chen@Sun.COM intr = usb_alloc_intr_req(ecmp->ecm_dip, 0, USB_FLAGS_SLEEP);
150411900SRaymond.Chen@Sun.COM
150511900SRaymond.Chen@Sun.COM /*
150611900SRaymond.Chen@Sun.COM * If it is in interrupt context, usb_alloc_intr_req will return NULL if
150711900SRaymond.Chen@Sun.COM * called with SLEEP flag.
150811900SRaymond.Chen@Sun.COM */
150911900SRaymond.Chen@Sun.COM if (!intr) {
151011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
151111900SRaymond.Chen@Sun.COM "usbecm_pipe_start_polling: alloc req failed.");
151211900SRaymond.Chen@Sun.COM
151311900SRaymond.Chen@Sun.COM return;
151411900SRaymond.Chen@Sun.COM }
151511900SRaymond.Chen@Sun.COM
151611900SRaymond.Chen@Sun.COM /* initialize the interrupt request. */
151711900SRaymond.Chen@Sun.COM intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
151811900SRaymond.Chen@Sun.COM USB_ATTRS_AUTOCLEARING;
151911900SRaymond.Chen@Sun.COM intr->intr_len = ecmp->ecm_intr_ep->ep_descr.wMaxPacketSize;
152011900SRaymond.Chen@Sun.COM intr->intr_client_private = (usb_opaque_t)ecmp;
152111900SRaymond.Chen@Sun.COM intr->intr_cb = usbecm_intr_cb;
152211900SRaymond.Chen@Sun.COM intr->intr_exc_cb = usbecm_intr_ex_cb;
152311900SRaymond.Chen@Sun.COM
152411900SRaymond.Chen@Sun.COM rval = usb_pipe_intr_xfer(ecmp->ecm_intr_ph, intr, USB_FLAGS_SLEEP);
152511900SRaymond.Chen@Sun.COM
152611900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
152711900SRaymond.Chen@Sun.COM if (rval == USB_SUCCESS) {
152811900SRaymond.Chen@Sun.COM ecmp->ecm_intr_state = USBECM_PIPE_BUSY;
152911900SRaymond.Chen@Sun.COM } else {
153011900SRaymond.Chen@Sun.COM usb_free_intr_req(intr);
153111900SRaymond.Chen@Sun.COM ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
153211900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
153311900SRaymond.Chen@Sun.COM "usbecm_pipe_start_polling: failed (%d)", rval);
153411900SRaymond.Chen@Sun.COM }
153511900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
153611900SRaymond.Chen@Sun.COM
153711900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
153811900SRaymond.Chen@Sun.COM "usbecm_pipe_start_polling: end, rval=%d", rval);
153911900SRaymond.Chen@Sun.COM }
154011900SRaymond.Chen@Sun.COM
154111900SRaymond.Chen@Sun.COM
154211900SRaymond.Chen@Sun.COM /*
154311900SRaymond.Chen@Sun.COM * usbsecm_intr_cb:
154411900SRaymond.Chen@Sun.COM * interrupt pipe normal callback
154511900SRaymond.Chen@Sun.COM */
154611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
154711900SRaymond.Chen@Sun.COM static void
usbecm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)154811900SRaymond.Chen@Sun.COM usbecm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
154911900SRaymond.Chen@Sun.COM {
155011900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
155111900SRaymond.Chen@Sun.COM mblk_t *data = req->intr_data;
155211900SRaymond.Chen@Sun.COM int data_len;
155311900SRaymond.Chen@Sun.COM
155411900SRaymond.Chen@Sun.COM data_len = (data) ? MBLKL(data) : 0;
155511900SRaymond.Chen@Sun.COM
155611900SRaymond.Chen@Sun.COM DTRACE_PROBE2(usbecm_intr__cb, (usb_intr_req_t *), req, int, data_len);
155711900SRaymond.Chen@Sun.COM
155811900SRaymond.Chen@Sun.COM /* check data length */
155911900SRaymond.Chen@Sun.COM if (data_len < 8) {
156011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
156111900SRaymond.Chen@Sun.COM "usbsecm_intr_cb: %d packet too short", data_len);
156211900SRaymond.Chen@Sun.COM usb_free_intr_req(req);
156311900SRaymond.Chen@Sun.COM
156411900SRaymond.Chen@Sun.COM return;
156511900SRaymond.Chen@Sun.COM }
156611900SRaymond.Chen@Sun.COM req->intr_data = NULL;
156711900SRaymond.Chen@Sun.COM usb_free_intr_req(req);
156811900SRaymond.Chen@Sun.COM
156911900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
157011900SRaymond.Chen@Sun.COM /* parse interrupt data -- notifications */
157111900SRaymond.Chen@Sun.COM usbecm_parse_intr_data(ecmp, data);
157211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
157311900SRaymond.Chen@Sun.COM }
157411900SRaymond.Chen@Sun.COM
157511900SRaymond.Chen@Sun.COM
157611900SRaymond.Chen@Sun.COM /*
157711900SRaymond.Chen@Sun.COM * usbsecm_intr_ex_cb:
157811900SRaymond.Chen@Sun.COM * interrupt pipe exception callback
157911900SRaymond.Chen@Sun.COM */
158011900SRaymond.Chen@Sun.COM /*ARGSUSED*/
158111900SRaymond.Chen@Sun.COM static void
usbecm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)158211900SRaymond.Chen@Sun.COM usbecm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
158311900SRaymond.Chen@Sun.COM {
158411900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)req->intr_client_private;
158511900SRaymond.Chen@Sun.COM usb_cr_t cr = req->intr_completion_reason;
158611900SRaymond.Chen@Sun.COM
158711900SRaymond.Chen@Sun.COM DTRACE_PROBE2(usbecm_intr_ex__cb, int, ecmp->ecm_dev_state,
158811900SRaymond.Chen@Sun.COM (usb_cr_t), cr);
158911900SRaymond.Chen@Sun.COM
159011900SRaymond.Chen@Sun.COM usb_free_intr_req(req);
159111900SRaymond.Chen@Sun.COM
159211900SRaymond.Chen@Sun.COM /*
159311900SRaymond.Chen@Sun.COM * If completion reason isn't USB_CR_PIPE_CLOSING and
159411900SRaymond.Chen@Sun.COM * USB_CR_STOPPED_POLLING, restart polling.
159511900SRaymond.Chen@Sun.COM */
159611900SRaymond.Chen@Sun.COM if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
159711900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
159811900SRaymond.Chen@Sun.COM
159911900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_ONLINE) {
160011900SRaymond.Chen@Sun.COM
160111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
160211900SRaymond.Chen@Sun.COM "usbsecm_intr_ex_cb: state = %d",
160311900SRaymond.Chen@Sun.COM ecmp->ecm_dev_state);
160411900SRaymond.Chen@Sun.COM
160511900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
160611900SRaymond.Chen@Sun.COM
160711900SRaymond.Chen@Sun.COM return;
160811900SRaymond.Chen@Sun.COM }
160911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
161011900SRaymond.Chen@Sun.COM
161111900SRaymond.Chen@Sun.COM usbecm_pipe_start_polling(ecmp);
161211900SRaymond.Chen@Sun.COM }
161311900SRaymond.Chen@Sun.COM }
161411900SRaymond.Chen@Sun.COM
161511900SRaymond.Chen@Sun.COM
161611900SRaymond.Chen@Sun.COM /*
161711900SRaymond.Chen@Sun.COM * usbsecm_parse_intr_data:
161811900SRaymond.Chen@Sun.COM * Parse data received from interrupt callback
161911900SRaymond.Chen@Sun.COM */
162011900SRaymond.Chen@Sun.COM static void
usbecm_parse_intr_data(usbecm_state_t * ecmp,mblk_t * data)162111900SRaymond.Chen@Sun.COM usbecm_parse_intr_data(usbecm_state_t *ecmp, mblk_t *data)
162211900SRaymond.Chen@Sun.COM {
162311900SRaymond.Chen@Sun.COM uint8_t bmRequestType;
162411900SRaymond.Chen@Sun.COM uint8_t bNotification;
162511900SRaymond.Chen@Sun.COM uint16_t wValue;
162611900SRaymond.Chen@Sun.COM uint16_t wLength;
162711900SRaymond.Chen@Sun.COM int linkstate;
162811900SRaymond.Chen@Sun.COM
162911900SRaymond.Chen@Sun.COM bmRequestType = data->b_rptr[0];
163011900SRaymond.Chen@Sun.COM bNotification = data->b_rptr[1];
163111900SRaymond.Chen@Sun.COM /*
163211900SRaymond.Chen@Sun.COM * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
163311900SRaymond.Chen@Sun.COM * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
163411900SRaymond.Chen@Sun.COM * mLength is 2. So we directly get the value from the byte.
163511900SRaymond.Chen@Sun.COM */
163611900SRaymond.Chen@Sun.COM wValue = data->b_rptr[2];
163711900SRaymond.Chen@Sun.COM wLength = data->b_rptr[6];
163811900SRaymond.Chen@Sun.COM
163911900SRaymond.Chen@Sun.COM if (ecmp->ecm_compatibility) {
164011900SRaymond.Chen@Sun.COM if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
164111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
164211900SRaymond.Chen@Sun.COM "usbsecm_parse_intr_data: unknown request "
164311900SRaymond.Chen@Sun.COM "type - 0x%x", bmRequestType);
164411900SRaymond.Chen@Sun.COM
164511900SRaymond.Chen@Sun.COM freemsg(data);
164611900SRaymond.Chen@Sun.COM
164711900SRaymond.Chen@Sun.COM return;
164811900SRaymond.Chen@Sun.COM }
164911900SRaymond.Chen@Sun.COM } else {
165011900SRaymond.Chen@Sun.COM /* non-compatible device specific parsing */
165111900SRaymond.Chen@Sun.COM if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) {
165211900SRaymond.Chen@Sun.COM if (ecmp->ecm_ds_ops->ecm_ds_intr_cb(ecmp, data)
165311900SRaymond.Chen@Sun.COM != USB_SUCCESS) {
165411900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CB, ecmp->ecm_lh,
165511900SRaymond.Chen@Sun.COM "usbsecm_parse_intr_data: unknown request"
165611900SRaymond.Chen@Sun.COM "type - 0x%x", bmRequestType);
165711900SRaymond.Chen@Sun.COM }
165811900SRaymond.Chen@Sun.COM }
165911900SRaymond.Chen@Sun.COM freemsg(data);
166011900SRaymond.Chen@Sun.COM
166111900SRaymond.Chen@Sun.COM return;
166211900SRaymond.Chen@Sun.COM }
166311900SRaymond.Chen@Sun.COM
166411900SRaymond.Chen@Sun.COM /*
166511900SRaymond.Chen@Sun.COM * Check the return value of compatible devices
166611900SRaymond.Chen@Sun.COM */
166711900SRaymond.Chen@Sun.COM switch (bNotification) {
166811900SRaymond.Chen@Sun.COM case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
166911900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
167011900SRaymond.Chen@Sun.COM "usbsecm_parse_intr_data: %s network!",
167111900SRaymond.Chen@Sun.COM wValue ? "connected to" :"disconnected from");
167211900SRaymond.Chen@Sun.COM
167311900SRaymond.Chen@Sun.COM linkstate = wValue ? LINK_STATE_UP:LINK_STATE_DOWN;
167411900SRaymond.Chen@Sun.COM if (ecmp->ecm_stat.es_linkstate == linkstate) {
167511900SRaymond.Chen@Sun.COM /* no changes to previous state */
167611900SRaymond.Chen@Sun.COM break;
167711900SRaymond.Chen@Sun.COM }
167811900SRaymond.Chen@Sun.COM
167911900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_linkstate = linkstate;
168011900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
168111900SRaymond.Chen@Sun.COM mac_link_update(ecmp->ecm_mh, linkstate);
168211900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
168311900SRaymond.Chen@Sun.COM
168411900SRaymond.Chen@Sun.COM break;
168511900SRaymond.Chen@Sun.COM case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
168611900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
168711900SRaymond.Chen@Sun.COM "usbsecm_parse_intr_data: A response is a available.");
168811900SRaymond.Chen@Sun.COM
168911900SRaymond.Chen@Sun.COM break;
169011900SRaymond.Chen@Sun.COM case USB_CDC_NOTIFICATION_SPEED_CHANGE:
169111900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
169211900SRaymond.Chen@Sun.COM "usbsecm_parse_intr_data: speed change");
169311900SRaymond.Chen@Sun.COM
169411900SRaymond.Chen@Sun.COM /* check the parameter's length. */
169511900SRaymond.Chen@Sun.COM if (wLength != 8) {
169611900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
169711900SRaymond.Chen@Sun.COM "usbsecm_parse_intr_data: error data length.");
169811900SRaymond.Chen@Sun.COM } else {
169911900SRaymond.Chen@Sun.COM uint32_t us_rate, ds_rate;
170011900SRaymond.Chen@Sun.COM uint8_t *sp;
170111900SRaymond.Chen@Sun.COM
170211900SRaymond.Chen@Sun.COM sp = &data->b_rptr[8];
170311900SRaymond.Chen@Sun.COM LE_TO_UINT32(sp, us_rate);
170411900SRaymond.Chen@Sun.COM sp = &data->b_rptr[12];
170511900SRaymond.Chen@Sun.COM LE_TO_UINT32(sp, ds_rate);
170611900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_upspeed = us_rate;
170711900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_downspeed = ds_rate;
170811900SRaymond.Chen@Sun.COM }
170911900SRaymond.Chen@Sun.COM
171011900SRaymond.Chen@Sun.COM break;
171111900SRaymond.Chen@Sun.COM default:
171211900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_CB, ecmp->ecm_lh,
171311900SRaymond.Chen@Sun.COM "usbsecm_parse_intr_data: unknown notification - 0x%x!",
171411900SRaymond.Chen@Sun.COM bNotification);
171511900SRaymond.Chen@Sun.COM
171611900SRaymond.Chen@Sun.COM break;
171711900SRaymond.Chen@Sun.COM }
171811900SRaymond.Chen@Sun.COM
171911900SRaymond.Chen@Sun.COM freemsg(data);
172011900SRaymond.Chen@Sun.COM }
172111900SRaymond.Chen@Sun.COM
172211900SRaymond.Chen@Sun.COM /*
172311900SRaymond.Chen@Sun.COM * usbecm_restore_device_state:
172411900SRaymond.Chen@Sun.COM * restore device state after CPR resume or reconnect
172511900SRaymond.Chen@Sun.COM */
172611900SRaymond.Chen@Sun.COM static int
usbecm_restore_device_state(usbecm_state_t * ecmp)172711900SRaymond.Chen@Sun.COM usbecm_restore_device_state(usbecm_state_t *ecmp)
172811900SRaymond.Chen@Sun.COM {
172911900SRaymond.Chen@Sun.COM int state;
173011900SRaymond.Chen@Sun.COM
173111900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
173211900SRaymond.Chen@Sun.COM "usbecm_restore_device_state: ");
173311900SRaymond.Chen@Sun.COM
173411900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
173511900SRaymond.Chen@Sun.COM state = ecmp->ecm_dev_state;
173611900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
173711900SRaymond.Chen@Sun.COM
173811900SRaymond.Chen@Sun.COM /* Check device status */
173911900SRaymond.Chen@Sun.COM if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
174011900SRaymond.Chen@Sun.COM
174111900SRaymond.Chen@Sun.COM return (state);
174211900SRaymond.Chen@Sun.COM }
174311900SRaymond.Chen@Sun.COM
174411900SRaymond.Chen@Sun.COM /* Check if we are talking to the same device */
174511900SRaymond.Chen@Sun.COM if (usb_check_same_device(ecmp->ecm_dip, ecmp->ecm_lh, USB_LOG_L0,
174611900SRaymond.Chen@Sun.COM -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
174711900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
174811900SRaymond.Chen@Sun.COM state = ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
174911900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
175011900SRaymond.Chen@Sun.COM
175111900SRaymond.Chen@Sun.COM return (state);
175211900SRaymond.Chen@Sun.COM }
175311900SRaymond.Chen@Sun.COM
175411900SRaymond.Chen@Sun.COM if (state == USB_DEV_DISCONNECTED) {
175511900SRaymond.Chen@Sun.COM USB_DPRINTF_L1(PRINT_MASK_EVENTS, ecmp->ecm_lh,
175611900SRaymond.Chen@Sun.COM "usbecm_restore_device_state: Device has been reconnected "
175711900SRaymond.Chen@Sun.COM "but data may have been lost");
175811900SRaymond.Chen@Sun.COM }
175911900SRaymond.Chen@Sun.COM
176011900SRaymond.Chen@Sun.COM /* if MAC was started, restarted it */
176111900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
176211900SRaymond.Chen@Sun.COM if (ecmp->ecm_mac_state == USBECM_MAC_STARTED) {
176311900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_EVENTS, ecmp->ecm_lh,
176411900SRaymond.Chen@Sun.COM "usbecm_restore_device_state: MAC was started");
176511900SRaymond.Chen@Sun.COM
176611900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
176711900SRaymond.Chen@Sun.COM /* Do the same operation as usbecm_m_start() does */
176811900SRaymond.Chen@Sun.COM if (usbecm_open_pipes(ecmp) != USB_SUCCESS) {
176911900SRaymond.Chen@Sun.COM
177011900SRaymond.Chen@Sun.COM return (state);
177111900SRaymond.Chen@Sun.COM }
177211900SRaymond.Chen@Sun.COM
177311900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
177411900SRaymond.Chen@Sun.COM if (usbecm_rx_start(ecmp) != USB_SUCCESS) {
177511900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
177611900SRaymond.Chen@Sun.COM
177711900SRaymond.Chen@Sun.COM return (state);
177811900SRaymond.Chen@Sun.COM }
177911900SRaymond.Chen@Sun.COM }
178011900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
178111900SRaymond.Chen@Sun.COM
178211900SRaymond.Chen@Sun.COM /*
178311900SRaymond.Chen@Sun.COM * init device state
178411900SRaymond.Chen@Sun.COM */
178511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
178611900SRaymond.Chen@Sun.COM state = ecmp->ecm_dev_state = USB_DEV_ONLINE;
178711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
178811900SRaymond.Chen@Sun.COM
178911900SRaymond.Chen@Sun.COM return (state);
179011900SRaymond.Chen@Sun.COM }
179111900SRaymond.Chen@Sun.COM
179211900SRaymond.Chen@Sun.COM /*
179311900SRaymond.Chen@Sun.COM * usbecm_reconnect_event_cb:
179411900SRaymond.Chen@Sun.COM * called upon when the device is hotplugged back
179511900SRaymond.Chen@Sun.COM */
179611900SRaymond.Chen@Sun.COM /*ARGSUSED*/
179711900SRaymond.Chen@Sun.COM static int
usbecm_reconnect_event_cb(dev_info_t * dip)179811900SRaymond.Chen@Sun.COM usbecm_reconnect_event_cb(dev_info_t *dip)
179911900SRaymond.Chen@Sun.COM {
180011900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp =
180111900SRaymond.Chen@Sun.COM (usbecm_state_t *)ddi_get_soft_state(usbecm_statep,
180211900SRaymond.Chen@Sun.COM ddi_get_instance(dip));
180311900SRaymond.Chen@Sun.COM
180411900SRaymond.Chen@Sun.COM ASSERT(ecmp != NULL);
180511900SRaymond.Chen@Sun.COM
180611900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
180711900SRaymond.Chen@Sun.COM "usbecm_reconnect_event_cb: entry");
180811900SRaymond.Chen@Sun.COM
180911900SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
181011900SRaymond.Chen@Sun.COM
181111900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
181211900SRaymond.Chen@Sun.COM ASSERT(ecmp->ecm_dev_state == USB_DEV_DISCONNECTED);
181311900SRaymond.Chen@Sun.COM
181411900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
181511900SRaymond.Chen@Sun.COM
181611900SRaymond.Chen@Sun.COM if (usbecm_restore_device_state(ecmp) != USB_DEV_ONLINE) {
181711900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
181811900SRaymond.Chen@Sun.COM
181911900SRaymond.Chen@Sun.COM return (USB_FAILURE);
182011900SRaymond.Chen@Sun.COM }
182111900SRaymond.Chen@Sun.COM
182211900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
182311900SRaymond.Chen@Sun.COM
182411900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
182511900SRaymond.Chen@Sun.COM }
182611900SRaymond.Chen@Sun.COM
182711900SRaymond.Chen@Sun.COM
182811900SRaymond.Chen@Sun.COM /*
182911900SRaymond.Chen@Sun.COM * usbecm_disconnect_event_cb:
183011900SRaymond.Chen@Sun.COM * callback for disconnect events
183111900SRaymond.Chen@Sun.COM */
183211900SRaymond.Chen@Sun.COM /*ARGSUSED*/
183311900SRaymond.Chen@Sun.COM static int
usbecm_disconnect_event_cb(dev_info_t * dip)183411900SRaymond.Chen@Sun.COM usbecm_disconnect_event_cb(dev_info_t *dip)
183511900SRaymond.Chen@Sun.COM {
183611900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp = (usbecm_state_t *)ddi_get_soft_state(
183711900SRaymond.Chen@Sun.COM usbecm_statep, ddi_get_instance(dip));
183811900SRaymond.Chen@Sun.COM
183911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
184011900SRaymond.Chen@Sun.COM "usbecm_disconnect_event_cb: entry");
184111900SRaymond.Chen@Sun.COM
184211900SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
184311900SRaymond.Chen@Sun.COM
184411900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
184511900SRaymond.Chen@Sun.COM ecmp->ecm_dev_state = USB_DEV_DISCONNECTED;
184611900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
184711900SRaymond.Chen@Sun.COM
184811900SRaymond.Chen@Sun.COM usbecm_close_pipes(ecmp);
184911900SRaymond.Chen@Sun.COM
185011900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
185111900SRaymond.Chen@Sun.COM
185211900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_EVENTS, ecmp->ecm_lh,
185311900SRaymond.Chen@Sun.COM "usbecm_disconnect_event_cb: End");
185411900SRaymond.Chen@Sun.COM
185511900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
185611900SRaymond.Chen@Sun.COM }
185711900SRaymond.Chen@Sun.COM
185811900SRaymond.Chen@Sun.COM /*
185911900SRaymond.Chen@Sun.COM * power management
186011900SRaymond.Chen@Sun.COM * ----------------
186111900SRaymond.Chen@Sun.COM *
186211900SRaymond.Chen@Sun.COM * usbecm_create_pm_components:
186311900SRaymond.Chen@Sun.COM * create PM components
186411900SRaymond.Chen@Sun.COM */
186511900SRaymond.Chen@Sun.COM static int
usbecm_create_pm_components(usbecm_state_t * ecmp)186611900SRaymond.Chen@Sun.COM usbecm_create_pm_components(usbecm_state_t *ecmp)
186711900SRaymond.Chen@Sun.COM {
186811900SRaymond.Chen@Sun.COM dev_info_t *dip = ecmp->ecm_dip;
186911900SRaymond.Chen@Sun.COM usbecm_pm_t *pm;
187011900SRaymond.Chen@Sun.COM uint_t pwr_states;
187111900SRaymond.Chen@Sun.COM
187211900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
187311900SRaymond.Chen@Sun.COM "usbecm_create_pm_components: entry");
187411900SRaymond.Chen@Sun.COM
187511900SRaymond.Chen@Sun.COM if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
187611900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
187711900SRaymond.Chen@Sun.COM "usbecm_create_pm_components: failed");
187811900SRaymond.Chen@Sun.COM
187911900SRaymond.Chen@Sun.COM /* don't fail the attach process */
188011900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
188111900SRaymond.Chen@Sun.COM }
188211900SRaymond.Chen@Sun.COM
188311900SRaymond.Chen@Sun.COM pm = ecmp->ecm_pm =
188411900SRaymond.Chen@Sun.COM (usbecm_pm_t *)kmem_zalloc(sizeof (usbecm_pm_t), KM_SLEEP);
188511900SRaymond.Chen@Sun.COM
188611900SRaymond.Chen@Sun.COM pm->pm_pwr_states = (uint8_t)pwr_states;
188711900SRaymond.Chen@Sun.COM pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
188811900SRaymond.Chen@Sun.COM pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
188911900SRaymond.Chen@Sun.COM USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
189011900SRaymond.Chen@Sun.COM
189111900SRaymond.Chen@Sun.COM (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
189211900SRaymond.Chen@Sun.COM
189311900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
189411900SRaymond.Chen@Sun.COM }
189511900SRaymond.Chen@Sun.COM
189611900SRaymond.Chen@Sun.COM /*
189711900SRaymond.Chen@Sun.COM * usbecm_cleanup:
189811900SRaymond.Chen@Sun.COM * Release resources of current device during detach.
189911900SRaymond.Chen@Sun.COM */
190011900SRaymond.Chen@Sun.COM static void
usbecm_cleanup(usbecm_state_t * ecmp)190111900SRaymond.Chen@Sun.COM usbecm_cleanup(usbecm_state_t *ecmp)
190211900SRaymond.Chen@Sun.COM {
190311900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
190411900SRaymond.Chen@Sun.COM "usbecm_cleanup: ");
190511900SRaymond.Chen@Sun.COM
190611900SRaymond.Chen@Sun.COM if (ecmp == NULL) {
190711900SRaymond.Chen@Sun.COM
190811900SRaymond.Chen@Sun.COM return;
190911900SRaymond.Chen@Sun.COM }
191011900SRaymond.Chen@Sun.COM
191111900SRaymond.Chen@Sun.COM usbecm_close_pipes(ecmp);
191211900SRaymond.Chen@Sun.COM
191311900SRaymond.Chen@Sun.COM /* unregister callback function */
191411900SRaymond.Chen@Sun.COM if (ecmp->ecm_init_flags & USBECM_INIT_EVENTS) {
191511900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
191611900SRaymond.Chen@Sun.COM "usbecm_cleanup: unregister events");
191711900SRaymond.Chen@Sun.COM
191811900SRaymond.Chen@Sun.COM usb_unregister_event_cbs(ecmp->ecm_dip, &usbecm_events);
191911900SRaymond.Chen@Sun.COM }
192011900SRaymond.Chen@Sun.COM
192111900SRaymond.Chen@Sun.COM /* destroy power management components */
192211900SRaymond.Chen@Sun.COM if (ecmp->ecm_pm != NULL) {
192311900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
192411900SRaymond.Chen@Sun.COM "usbecm_cleanup: destroy pm");
192511900SRaymond.Chen@Sun.COM usbecm_destroy_pm_components(ecmp);
192611900SRaymond.Chen@Sun.COM }
192711900SRaymond.Chen@Sun.COM
192811900SRaymond.Chen@Sun.COM /* free description of device tree. */
192911900SRaymond.Chen@Sun.COM if (ecmp->ecm_def_ph != NULL) {
193011900SRaymond.Chen@Sun.COM mutex_destroy(&ecmp->ecm_mutex);
193111900SRaymond.Chen@Sun.COM
193211900SRaymond.Chen@Sun.COM usb_free_descr_tree(ecmp->ecm_dip, ecmp->ecm_dev_data);
193311900SRaymond.Chen@Sun.COM ecmp->ecm_def_ph = NULL;
193411900SRaymond.Chen@Sun.COM }
193511900SRaymond.Chen@Sun.COM
193611900SRaymond.Chen@Sun.COM if (ecmp->ecm_lh != NULL) {
193711900SRaymond.Chen@Sun.COM usb_free_log_hdl(ecmp->ecm_lh);
193811900SRaymond.Chen@Sun.COM ecmp->ecm_lh = NULL;
193911900SRaymond.Chen@Sun.COM }
194011900SRaymond.Chen@Sun.COM
194111900SRaymond.Chen@Sun.COM /* detach client device */
194211900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_data != NULL) {
194311900SRaymond.Chen@Sun.COM usb_client_detach(ecmp->ecm_dip, ecmp->ecm_dev_data);
194411900SRaymond.Chen@Sun.COM }
194511900SRaymond.Chen@Sun.COM
194611900SRaymond.Chen@Sun.COM if (ecmp->ecm_init_flags & USBECM_INIT_MAC) {
1947*11902SRaymond.Chen@Sun.COM (void) usbecm_mac_fini(ecmp);
194811900SRaymond.Chen@Sun.COM }
194911900SRaymond.Chen@Sun.COM
195011900SRaymond.Chen@Sun.COM if (ecmp->ecm_init_flags & USBECM_INIT_SER) {
195111900SRaymond.Chen@Sun.COM usb_fini_serialization(ecmp->ecm_ser_acc);
195211900SRaymond.Chen@Sun.COM }
195311900SRaymond.Chen@Sun.COM
195411900SRaymond.Chen@Sun.COM ddi_prop_remove_all(ecmp->ecm_dip);
195511900SRaymond.Chen@Sun.COM ddi_remove_minor_node(ecmp->ecm_dip, NULL);
195611900SRaymond.Chen@Sun.COM }
195711900SRaymond.Chen@Sun.COM
195811900SRaymond.Chen@Sun.COM /*
195911900SRaymond.Chen@Sun.COM * usbecm_destroy_pm_components:
196011900SRaymond.Chen@Sun.COM * destroy PM components
196111900SRaymond.Chen@Sun.COM */
196211900SRaymond.Chen@Sun.COM static void
usbecm_destroy_pm_components(usbecm_state_t * ecmp)196311900SRaymond.Chen@Sun.COM usbecm_destroy_pm_components(usbecm_state_t *ecmp)
196411900SRaymond.Chen@Sun.COM {
196511900SRaymond.Chen@Sun.COM usbecm_pm_t *pm = ecmp->ecm_pm;
196611900SRaymond.Chen@Sun.COM dev_info_t *dip = ecmp->ecm_dip;
196711900SRaymond.Chen@Sun.COM int rval;
196811900SRaymond.Chen@Sun.COM
196911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
197011900SRaymond.Chen@Sun.COM "usbecm_destroy_pm_components: ");
197111900SRaymond.Chen@Sun.COM
197211900SRaymond.Chen@Sun.COM if (ecmp->ecm_dev_state != USB_DEV_DISCONNECTED) {
197311900SRaymond.Chen@Sun.COM if (pm->pm_wakeup_enabled) {
197411900SRaymond.Chen@Sun.COM rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
197511900SRaymond.Chen@Sun.COM if (rval != DDI_SUCCESS) {
197611900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
197711900SRaymond.Chen@Sun.COM "usbecm_destroy_pm_components: "
197811900SRaymond.Chen@Sun.COM "raising power failed (%d)", rval);
197911900SRaymond.Chen@Sun.COM }
198011900SRaymond.Chen@Sun.COM
198111900SRaymond.Chen@Sun.COM rval = usb_handle_remote_wakeup(dip,
198211900SRaymond.Chen@Sun.COM USB_REMOTE_WAKEUP_DISABLE);
198311900SRaymond.Chen@Sun.COM if (rval != USB_SUCCESS) {
198411900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
198511900SRaymond.Chen@Sun.COM "usbecm_destroy_pm_components: "
198611900SRaymond.Chen@Sun.COM "disable remote wakeup failed (%d)", rval);
198711900SRaymond.Chen@Sun.COM }
198811900SRaymond.Chen@Sun.COM }
198911900SRaymond.Chen@Sun.COM
199011900SRaymond.Chen@Sun.COM (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
199111900SRaymond.Chen@Sun.COM }
199211900SRaymond.Chen@Sun.COM kmem_free((caddr_t)pm, sizeof (usbecm_pm_t));
199311900SRaymond.Chen@Sun.COM ecmp->ecm_pm = NULL;
199411900SRaymond.Chen@Sun.COM }
199511900SRaymond.Chen@Sun.COM
199611900SRaymond.Chen@Sun.COM /*
199711900SRaymond.Chen@Sun.COM * usbecm_pm_set_busy:
199811900SRaymond.Chen@Sun.COM * mark device busy and raise power
199911900SRaymond.Chen@Sun.COM */
200011900SRaymond.Chen@Sun.COM static void
usbecm_pm_set_busy(usbecm_state_t * ecmp)200111900SRaymond.Chen@Sun.COM usbecm_pm_set_busy(usbecm_state_t *ecmp)
200211900SRaymond.Chen@Sun.COM {
200311900SRaymond.Chen@Sun.COM usbecm_pm_t *pm = ecmp->ecm_pm;
200411900SRaymond.Chen@Sun.COM dev_info_t *dip = ecmp->ecm_dip;
200511900SRaymond.Chen@Sun.COM int rval;
200611900SRaymond.Chen@Sun.COM
200711900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
200811900SRaymond.Chen@Sun.COM "usbecm_pm_set_busy: pm = 0x%p", (void *)pm);
200911900SRaymond.Chen@Sun.COM
201011900SRaymond.Chen@Sun.COM if (pm == NULL) {
201111900SRaymond.Chen@Sun.COM
201211900SRaymond.Chen@Sun.COM return;
201311900SRaymond.Chen@Sun.COM }
201411900SRaymond.Chen@Sun.COM
201511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
201611900SRaymond.Chen@Sun.COM /* if already marked busy, just increment the counter */
201711900SRaymond.Chen@Sun.COM if (pm->pm_busy_cnt++ > 0) {
201811900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
201911900SRaymond.Chen@Sun.COM
202011900SRaymond.Chen@Sun.COM return;
202111900SRaymond.Chen@Sun.COM }
202211900SRaymond.Chen@Sun.COM
202311900SRaymond.Chen@Sun.COM (void) pm_busy_component(dip, 0);
202411900SRaymond.Chen@Sun.COM
202511900SRaymond.Chen@Sun.COM if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
202611900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
202711900SRaymond.Chen@Sun.COM
202811900SRaymond.Chen@Sun.COM return;
202911900SRaymond.Chen@Sun.COM }
203011900SRaymond.Chen@Sun.COM
203111900SRaymond.Chen@Sun.COM /* need to raise power */
203211900SRaymond.Chen@Sun.COM pm->pm_raise_power = B_TRUE;
203311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
203411900SRaymond.Chen@Sun.COM
203511900SRaymond.Chen@Sun.COM rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
203611900SRaymond.Chen@Sun.COM if (rval != DDI_SUCCESS) {
203711900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
203811900SRaymond.Chen@Sun.COM "usbecm_pm_set_busy: raising power failed");
203911900SRaymond.Chen@Sun.COM }
204011900SRaymond.Chen@Sun.COM
204111900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
204211900SRaymond.Chen@Sun.COM pm->pm_raise_power = B_FALSE;
204311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
204411900SRaymond.Chen@Sun.COM }
204511900SRaymond.Chen@Sun.COM
204611900SRaymond.Chen@Sun.COM
204711900SRaymond.Chen@Sun.COM /*
204811900SRaymond.Chen@Sun.COM * usbecm_pm_set_idle:
204911900SRaymond.Chen@Sun.COM * mark device idle
205011900SRaymond.Chen@Sun.COM */
205111900SRaymond.Chen@Sun.COM static void
usbecm_pm_set_idle(usbecm_state_t * ecmp)205211900SRaymond.Chen@Sun.COM usbecm_pm_set_idle(usbecm_state_t *ecmp)
205311900SRaymond.Chen@Sun.COM {
205411900SRaymond.Chen@Sun.COM usbecm_pm_t *pm = ecmp->ecm_pm;
205511900SRaymond.Chen@Sun.COM dev_info_t *dip = ecmp->ecm_dip;
205611900SRaymond.Chen@Sun.COM
205711900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
205811900SRaymond.Chen@Sun.COM "usbecm_pm_set_idle: ");
205911900SRaymond.Chen@Sun.COM
206011900SRaymond.Chen@Sun.COM if (pm == NULL) {
206111900SRaymond.Chen@Sun.COM
206211900SRaymond.Chen@Sun.COM return;
206311900SRaymond.Chen@Sun.COM }
206411900SRaymond.Chen@Sun.COM
206511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
206611900SRaymond.Chen@Sun.COM if (--pm->pm_busy_cnt > 0) {
206711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
206811900SRaymond.Chen@Sun.COM
206911900SRaymond.Chen@Sun.COM return;
207011900SRaymond.Chen@Sun.COM }
207111900SRaymond.Chen@Sun.COM
207211900SRaymond.Chen@Sun.COM if (pm) {
207311900SRaymond.Chen@Sun.COM (void) pm_idle_component(dip, 0);
207411900SRaymond.Chen@Sun.COM }
207511900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
207611900SRaymond.Chen@Sun.COM }
207711900SRaymond.Chen@Sun.COM
207811900SRaymond.Chen@Sun.COM
207911900SRaymond.Chen@Sun.COM /*
208011900SRaymond.Chen@Sun.COM * usbecm_pwrlvl0:
208111900SRaymond.Chen@Sun.COM * Functions to handle power transition for OS levels 0 -> 3
208211900SRaymond.Chen@Sun.COM * The same level as OS state, different from USB state
208311900SRaymond.Chen@Sun.COM */
208411900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl0(usbecm_state_t * ecmp)208511900SRaymond.Chen@Sun.COM usbecm_pwrlvl0(usbecm_state_t *ecmp)
208611900SRaymond.Chen@Sun.COM {
208711900SRaymond.Chen@Sun.COM int rval;
208811900SRaymond.Chen@Sun.COM
208911900SRaymond.Chen@Sun.COM ASSERT(mutex_owned(&ecmp->ecm_mutex));
209011900SRaymond.Chen@Sun.COM
209111900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
209211900SRaymond.Chen@Sun.COM "usbecm_pwrlvl0: ");
209311900SRaymond.Chen@Sun.COM
209411900SRaymond.Chen@Sun.COM switch (ecmp->ecm_dev_state) {
209511900SRaymond.Chen@Sun.COM case USB_DEV_ONLINE:
209611900SRaymond.Chen@Sun.COM /* issue USB D3 command to the device */
209711900SRaymond.Chen@Sun.COM rval = usb_set_device_pwrlvl3(ecmp->ecm_dip);
209811900SRaymond.Chen@Sun.COM ASSERT(rval == USB_SUCCESS);
209911900SRaymond.Chen@Sun.COM if ((ecmp->ecm_intr_ph != NULL) &&
210011900SRaymond.Chen@Sun.COM (ecmp->ecm_intr_state == USBECM_PIPE_BUSY)) {
210111900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
210211900SRaymond.Chen@Sun.COM usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
210311900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP);
210411900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
210511900SRaymond.Chen@Sun.COM
210611900SRaymond.Chen@Sun.COM ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
210711900SRaymond.Chen@Sun.COM }
210811900SRaymond.Chen@Sun.COM ecmp->ecm_dev_state = USB_DEV_PWRED_DOWN;
210911900SRaymond.Chen@Sun.COM ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
211011900SRaymond.Chen@Sun.COM
211111900SRaymond.Chen@Sun.COM /* FALLTHRU */
211211900SRaymond.Chen@Sun.COM case USB_DEV_DISCONNECTED:
211311900SRaymond.Chen@Sun.COM case USB_DEV_SUSPENDED:
211411900SRaymond.Chen@Sun.COM /* allow a disconnect/cpr'ed device to go to lower power */
211511900SRaymond.Chen@Sun.COM
211611900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
211711900SRaymond.Chen@Sun.COM case USB_DEV_PWRED_DOWN:
211811900SRaymond.Chen@Sun.COM default:
211911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
212011900SRaymond.Chen@Sun.COM "usbecm_pwrlvl0: illegal device state");
212111900SRaymond.Chen@Sun.COM
212211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
212311900SRaymond.Chen@Sun.COM }
212411900SRaymond.Chen@Sun.COM }
212511900SRaymond.Chen@Sun.COM
212611900SRaymond.Chen@Sun.COM
212711900SRaymond.Chen@Sun.COM /*
212811900SRaymond.Chen@Sun.COM * usbecm_pwrlvl1:
212911900SRaymond.Chen@Sun.COM * Functions to handle power transition for OS levels 1 -> 2
213011900SRaymond.Chen@Sun.COM */
213111900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl1(usbecm_state_t * ecmp)213211900SRaymond.Chen@Sun.COM usbecm_pwrlvl1(usbecm_state_t *ecmp)
213311900SRaymond.Chen@Sun.COM {
213411900SRaymond.Chen@Sun.COM /* issue USB D2 command to the device */
213511900SRaymond.Chen@Sun.COM (void) usb_set_device_pwrlvl2(ecmp->ecm_dip);
213611900SRaymond.Chen@Sun.COM
213711900SRaymond.Chen@Sun.COM return (USB_FAILURE);
213811900SRaymond.Chen@Sun.COM }
213911900SRaymond.Chen@Sun.COM
214011900SRaymond.Chen@Sun.COM
214111900SRaymond.Chen@Sun.COM /*
214211900SRaymond.Chen@Sun.COM * usbecm_pwrlvl2:
214311900SRaymond.Chen@Sun.COM * Functions to handle power transition for OS levels 2 -> 1
214411900SRaymond.Chen@Sun.COM */
214511900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl2(usbecm_state_t * ecmp)214611900SRaymond.Chen@Sun.COM usbecm_pwrlvl2(usbecm_state_t *ecmp)
214711900SRaymond.Chen@Sun.COM {
214811900SRaymond.Chen@Sun.COM /* issue USB D1 command to the device */
214911900SRaymond.Chen@Sun.COM (void) usb_set_device_pwrlvl1(ecmp->ecm_dip);
215011900SRaymond.Chen@Sun.COM
215111900SRaymond.Chen@Sun.COM return (USB_FAILURE);
215211900SRaymond.Chen@Sun.COM }
215311900SRaymond.Chen@Sun.COM
215411900SRaymond.Chen@Sun.COM
215511900SRaymond.Chen@Sun.COM /*
215611900SRaymond.Chen@Sun.COM * usbecm_pwrlvl3:
215711900SRaymond.Chen@Sun.COM * Functions to handle power transition for OS levels 3 -> 0
215811900SRaymond.Chen@Sun.COM * The same level as OS state, different from USB state
215911900SRaymond.Chen@Sun.COM */
216011900SRaymond.Chen@Sun.COM static int
usbecm_pwrlvl3(usbecm_state_t * ecmp)216111900SRaymond.Chen@Sun.COM usbecm_pwrlvl3(usbecm_state_t *ecmp)
216211900SRaymond.Chen@Sun.COM {
216311900SRaymond.Chen@Sun.COM int rval;
216411900SRaymond.Chen@Sun.COM
216511900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
216611900SRaymond.Chen@Sun.COM "usbecm_pwrlvl3: ");
216711900SRaymond.Chen@Sun.COM
216811900SRaymond.Chen@Sun.COM ASSERT(mutex_owned(&ecmp->ecm_mutex));
216911900SRaymond.Chen@Sun.COM
217011900SRaymond.Chen@Sun.COM switch (ecmp->ecm_dev_state) {
217111900SRaymond.Chen@Sun.COM case USB_DEV_PWRED_DOWN:
217211900SRaymond.Chen@Sun.COM /* Issue USB D0 command to the device here */
217311900SRaymond.Chen@Sun.COM rval = usb_set_device_pwrlvl0(ecmp->ecm_dip);
217411900SRaymond.Chen@Sun.COM ASSERT(rval == USB_SUCCESS);
217511900SRaymond.Chen@Sun.COM
217611900SRaymond.Chen@Sun.COM if (ecmp->ecm_intr_ph != NULL &&
217711900SRaymond.Chen@Sun.COM ecmp->ecm_intr_state == USBECM_PIPE_IDLE) {
217811900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
217911900SRaymond.Chen@Sun.COM usbecm_pipe_start_polling(ecmp);
218011900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
218111900SRaymond.Chen@Sun.COM }
218211900SRaymond.Chen@Sun.COM
218311900SRaymond.Chen@Sun.COM ecmp->ecm_dev_state = USB_DEV_ONLINE;
218411900SRaymond.Chen@Sun.COM ecmp->ecm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
218511900SRaymond.Chen@Sun.COM
218611900SRaymond.Chen@Sun.COM /* FALLTHRU */
218711900SRaymond.Chen@Sun.COM case USB_DEV_ONLINE:
218811900SRaymond.Chen@Sun.COM /* we are already in full power */
218911900SRaymond.Chen@Sun.COM
219011900SRaymond.Chen@Sun.COM /* FALLTHRU */
219111900SRaymond.Chen@Sun.COM case USB_DEV_DISCONNECTED:
219211900SRaymond.Chen@Sun.COM case USB_DEV_SUSPENDED:
219311900SRaymond.Chen@Sun.COM
219411900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
219511900SRaymond.Chen@Sun.COM default:
219611900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
219711900SRaymond.Chen@Sun.COM "usbecm_pwrlvl3: illegal device state");
219811900SRaymond.Chen@Sun.COM
219911900SRaymond.Chen@Sun.COM return (USB_FAILURE);
220011900SRaymond.Chen@Sun.COM }
220111900SRaymond.Chen@Sun.COM }
220211900SRaymond.Chen@Sun.COM
220311900SRaymond.Chen@Sun.COM /*ARGSUSED*/
220411900SRaymond.Chen@Sun.COM static int
usbecm_power(dev_info_t * dip,int comp,int level)220511900SRaymond.Chen@Sun.COM usbecm_power(dev_info_t *dip, int comp, int level)
220611900SRaymond.Chen@Sun.COM {
220711900SRaymond.Chen@Sun.COM usbecm_state_t *ecmp;
220811900SRaymond.Chen@Sun.COM usbecm_pm_t *pm;
220911900SRaymond.Chen@Sun.COM int rval = USB_SUCCESS;
221011900SRaymond.Chen@Sun.COM
221111900SRaymond.Chen@Sun.COM ecmp = ddi_get_soft_state(usbecm_statep, ddi_get_instance(dip));
221211900SRaymond.Chen@Sun.COM pm = ecmp->ecm_pm;
221311900SRaymond.Chen@Sun.COM
221411900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
221511900SRaymond.Chen@Sun.COM "usbecm_power: entry");
221611900SRaymond.Chen@Sun.COM
221711900SRaymond.Chen@Sun.COM /* check if pm is NULL */
221811900SRaymond.Chen@Sun.COM if (pm == NULL) {
221911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
222011900SRaymond.Chen@Sun.COM "usbecm_power: pm is NULL.");
222111900SRaymond.Chen@Sun.COM
222211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
222311900SRaymond.Chen@Sun.COM }
222411900SRaymond.Chen@Sun.COM
222511900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
222611900SRaymond.Chen@Sun.COM /*
222711900SRaymond.Chen@Sun.COM * check if we are transitioning to a legal power level
222811900SRaymond.Chen@Sun.COM */
222911900SRaymond.Chen@Sun.COM if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
223011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
223111900SRaymond.Chen@Sun.COM "usbecm_power: "
223211900SRaymond.Chen@Sun.COM "illegal power level %d, pwr_states=%x",
223311900SRaymond.Chen@Sun.COM level, pm->pm_pwr_states);
223411900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
223511900SRaymond.Chen@Sun.COM
223611900SRaymond.Chen@Sun.COM return (USB_FAILURE);
223711900SRaymond.Chen@Sun.COM }
223811900SRaymond.Chen@Sun.COM
223911900SRaymond.Chen@Sun.COM /*
224011900SRaymond.Chen@Sun.COM * if we are about to raise power and asked to lower power, fail
224111900SRaymond.Chen@Sun.COM */
224211900SRaymond.Chen@Sun.COM if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
224311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_PM, ecmp->ecm_lh,
224411900SRaymond.Chen@Sun.COM "usbecm_power: wrong condition.");
224511900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
224611900SRaymond.Chen@Sun.COM
224711900SRaymond.Chen@Sun.COM return (USB_FAILURE);
224811900SRaymond.Chen@Sun.COM }
224911900SRaymond.Chen@Sun.COM
225011900SRaymond.Chen@Sun.COM /*
225111900SRaymond.Chen@Sun.COM * Set the power status of device by request level.
225211900SRaymond.Chen@Sun.COM */
225311900SRaymond.Chen@Sun.COM switch (level) {
225411900SRaymond.Chen@Sun.COM case USB_DEV_OS_PWR_OFF:
225511900SRaymond.Chen@Sun.COM rval = usbecm_pwrlvl0(ecmp);
225611900SRaymond.Chen@Sun.COM
225711900SRaymond.Chen@Sun.COM break;
225811900SRaymond.Chen@Sun.COM case USB_DEV_OS_PWR_1:
225911900SRaymond.Chen@Sun.COM rval = usbecm_pwrlvl1(ecmp);
226011900SRaymond.Chen@Sun.COM
226111900SRaymond.Chen@Sun.COM break;
226211900SRaymond.Chen@Sun.COM case USB_DEV_OS_PWR_2:
226311900SRaymond.Chen@Sun.COM rval = usbecm_pwrlvl2(ecmp);
226411900SRaymond.Chen@Sun.COM
226511900SRaymond.Chen@Sun.COM break;
226611900SRaymond.Chen@Sun.COM case USB_DEV_OS_FULL_PWR:
226711900SRaymond.Chen@Sun.COM rval = usbecm_pwrlvl3(ecmp);
226811900SRaymond.Chen@Sun.COM
226911900SRaymond.Chen@Sun.COM break;
227011900SRaymond.Chen@Sun.COM }
227111900SRaymond.Chen@Sun.COM
227211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
227311900SRaymond.Chen@Sun.COM
227411900SRaymond.Chen@Sun.COM return (rval);
227511900SRaymond.Chen@Sun.COM }
227611900SRaymond.Chen@Sun.COM
227711900SRaymond.Chen@Sun.COM /*
227811900SRaymond.Chen@Sun.COM * Register with the MAC layer.
227911900SRaymond.Chen@Sun.COM */
228011900SRaymond.Chen@Sun.COM static int
usbecm_mac_init(usbecm_state_t * ecmp)228111900SRaymond.Chen@Sun.COM usbecm_mac_init(usbecm_state_t *ecmp)
228211900SRaymond.Chen@Sun.COM {
228311900SRaymond.Chen@Sun.COM mac_register_t *macp;
228411900SRaymond.Chen@Sun.COM int err;
228511900SRaymond.Chen@Sun.COM
228611900SRaymond.Chen@Sun.COM /*
228711900SRaymond.Chen@Sun.COM * Initialize mac structure
228811900SRaymond.Chen@Sun.COM */
228911900SRaymond.Chen@Sun.COM macp = mac_alloc(MAC_VERSION);
229011900SRaymond.Chen@Sun.COM if (macp == NULL) {
229111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
229211900SRaymond.Chen@Sun.COM "failed to allocate MAC structure");
229311900SRaymond.Chen@Sun.COM
229411900SRaymond.Chen@Sun.COM return (USB_FAILURE);
229511900SRaymond.Chen@Sun.COM }
229611900SRaymond.Chen@Sun.COM
229711900SRaymond.Chen@Sun.COM /*
229811900SRaymond.Chen@Sun.COM * Initialize pointer to device specific functions
229911900SRaymond.Chen@Sun.COM */
230011900SRaymond.Chen@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
230111900SRaymond.Chen@Sun.COM macp->m_driver = ecmp;
230211900SRaymond.Chen@Sun.COM macp->m_dip = ecmp->ecm_dip;
230311900SRaymond.Chen@Sun.COM
230411900SRaymond.Chen@Sun.COM macp->m_src_addr = ecmp->ecm_srcaddr;
230511900SRaymond.Chen@Sun.COM macp->m_callbacks = &usbecm_m_callbacks;
230611900SRaymond.Chen@Sun.COM macp->m_min_sdu = 0;
230711900SRaymond.Chen@Sun.COM macp->m_max_sdu = ETHERMTU;
230811900SRaymond.Chen@Sun.COM
230911900SRaymond.Chen@Sun.COM /*
231011900SRaymond.Chen@Sun.COM * Register the macp to mac
231111900SRaymond.Chen@Sun.COM */
231211900SRaymond.Chen@Sun.COM err = mac_register(macp, &ecmp->ecm_mh);
231311900SRaymond.Chen@Sun.COM mac_free(macp);
231411900SRaymond.Chen@Sun.COM
231511900SRaymond.Chen@Sun.COM if (err != DDI_SUCCESS) {
231611900SRaymond.Chen@Sun.COM USB_DPRINTF_L1(PRINT_MASK_ATTA, ecmp->ecm_lh,
231711900SRaymond.Chen@Sun.COM "failed to register MAC structure");
231811900SRaymond.Chen@Sun.COM
231911900SRaymond.Chen@Sun.COM return (USB_FAILURE);
232011900SRaymond.Chen@Sun.COM }
232111900SRaymond.Chen@Sun.COM
232211900SRaymond.Chen@Sun.COM mac_link_update(ecmp->ecm_mh, LINK_STATE_DOWN);
232311900SRaymond.Chen@Sun.COM ecmp->ecm_stat.es_linkstate = LINK_STATE_DOWN;
232411900SRaymond.Chen@Sun.COM ecmp->ecm_tx_cnt = 0;
232511900SRaymond.Chen@Sun.COM
232611900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
232711900SRaymond.Chen@Sun.COM }
232811900SRaymond.Chen@Sun.COM
232911900SRaymond.Chen@Sun.COM static int
usbecm_mac_fini(usbecm_state_t * ecmp)233011900SRaymond.Chen@Sun.COM usbecm_mac_fini(usbecm_state_t *ecmp)
233111900SRaymond.Chen@Sun.COM {
233211900SRaymond.Chen@Sun.COM int rval = DDI_SUCCESS;
233311900SRaymond.Chen@Sun.COM
233411900SRaymond.Chen@Sun.COM if ((ecmp->ecm_init_flags & USBECM_INIT_MAC) == 0) {
233511900SRaymond.Chen@Sun.COM return (DDI_SUCCESS);
233611900SRaymond.Chen@Sun.COM }
233711900SRaymond.Chen@Sun.COM
233811900SRaymond.Chen@Sun.COM ecmp->ecm_init_flags &= ~USBECM_INIT_MAC;
233911900SRaymond.Chen@Sun.COM if ((rval = mac_disable(ecmp->ecm_mh)) != 0) {
234011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
234111900SRaymond.Chen@Sun.COM "failed to disable MAC");
234211900SRaymond.Chen@Sun.COM
234311900SRaymond.Chen@Sun.COM return (rval);
234411900SRaymond.Chen@Sun.COM }
234511900SRaymond.Chen@Sun.COM
234611900SRaymond.Chen@Sun.COM (void) mac_unregister(ecmp->ecm_mh);
234711900SRaymond.Chen@Sun.COM
234811900SRaymond.Chen@Sun.COM return (rval);
234911900SRaymond.Chen@Sun.COM }
235011900SRaymond.Chen@Sun.COM
235111900SRaymond.Chen@Sun.COM static int
usbecm_resume(usbecm_state_t * ecmp)235211900SRaymond.Chen@Sun.COM usbecm_resume(usbecm_state_t *ecmp)
235311900SRaymond.Chen@Sun.COM {
235411900SRaymond.Chen@Sun.COM int current_state;
235511900SRaymond.Chen@Sun.COM int ret;
235611900SRaymond.Chen@Sun.COM
235711900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_PM, ecmp->ecm_lh,
235811900SRaymond.Chen@Sun.COM "usbecm_resume: ");
235911900SRaymond.Chen@Sun.COM
236011900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
236111900SRaymond.Chen@Sun.COM current_state = ecmp->ecm_dev_state;
236211900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
236311900SRaymond.Chen@Sun.COM
236411900SRaymond.Chen@Sun.COM /* restore the status of device */
236511900SRaymond.Chen@Sun.COM if (current_state != USB_DEV_ONLINE) {
236611900SRaymond.Chen@Sun.COM ret = usbecm_restore_device_state(ecmp);
236711900SRaymond.Chen@Sun.COM } else {
236811900SRaymond.Chen@Sun.COM ret = USB_DEV_ONLINE;
236911900SRaymond.Chen@Sun.COM }
237011900SRaymond.Chen@Sun.COM
237111900SRaymond.Chen@Sun.COM return (ret);
237211900SRaymond.Chen@Sun.COM }
237311900SRaymond.Chen@Sun.COM
237411900SRaymond.Chen@Sun.COM static int
usbecm_suspend(usbecm_state_t * ecmp)237511900SRaymond.Chen@Sun.COM usbecm_suspend(usbecm_state_t *ecmp)
237611900SRaymond.Chen@Sun.COM {
237711900SRaymond.Chen@Sun.COM (void) usb_serialize_access(ecmp->ecm_ser_acc, USB_WAIT, 0);
237811900SRaymond.Chen@Sun.COM
237911900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
238011900SRaymond.Chen@Sun.COM ecmp->ecm_dev_state = USB_DEV_SUSPENDED;
238111900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
238211900SRaymond.Chen@Sun.COM
238311900SRaymond.Chen@Sun.COM usbecm_close_pipes(ecmp);
238411900SRaymond.Chen@Sun.COM
238511900SRaymond.Chen@Sun.COM usb_release_access(ecmp->ecm_ser_acc);
238611900SRaymond.Chen@Sun.COM
238711900SRaymond.Chen@Sun.COM return (0);
238811900SRaymond.Chen@Sun.COM }
238911900SRaymond.Chen@Sun.COM
239011900SRaymond.Chen@Sun.COM /*
239111900SRaymond.Chen@Sun.COM * Translate MAC address from string to 6 bytes array int value
239211900SRaymond.Chen@Sun.COM * Can't use ether_aton() since it requires format of x:x:x:x:x:x
239311900SRaymond.Chen@Sun.COM */
239411900SRaymond.Chen@Sun.COM void
label_to_mac(char * hex,unsigned char * mac)239511900SRaymond.Chen@Sun.COM label_to_mac(char *hex, unsigned char *mac)
239611900SRaymond.Chen@Sun.COM {
239711900SRaymond.Chen@Sun.COM int i;
239811900SRaymond.Chen@Sun.COM char c;
239911900SRaymond.Chen@Sun.COM
240011900SRaymond.Chen@Sun.COM /* can only count 6 bytes! */
240111900SRaymond.Chen@Sun.COM for (i = 0; i < 6; i++) {
240211900SRaymond.Chen@Sun.COM /* upper 4 bits */
240311900SRaymond.Chen@Sun.COM if (!isdigit(hex[2*i])) {
240411900SRaymond.Chen@Sun.COM c = (toupper(hex[2 * i]) - 'A' + 10);
240511900SRaymond.Chen@Sun.COM } else {
240611900SRaymond.Chen@Sun.COM c = (hex[2 * i] - '0');
240711900SRaymond.Chen@Sun.COM }
240811900SRaymond.Chen@Sun.COM mac[i] = c * 16;
240911900SRaymond.Chen@Sun.COM
241011900SRaymond.Chen@Sun.COM /* lower 4 bits */
241111900SRaymond.Chen@Sun.COM if (!isdigit(hex[2*i + 1])) {
241211900SRaymond.Chen@Sun.COM c = (toupper(hex[2 * i + 1]) - 'A' + 10);
241311900SRaymond.Chen@Sun.COM } else {
241411900SRaymond.Chen@Sun.COM c = hex[2 * i + 1] - '0';
241511900SRaymond.Chen@Sun.COM }
241611900SRaymond.Chen@Sun.COM mac[i] += c;
241711900SRaymond.Chen@Sun.COM }
241811900SRaymond.Chen@Sun.COM }
241911900SRaymond.Chen@Sun.COM
242011900SRaymond.Chen@Sun.COM /*
242111900SRaymond.Chen@Sun.COM * usbecm_get_descriptors:
242211900SRaymond.Chen@Sun.COM * parse functional descriptors of ecm compatible device
242311900SRaymond.Chen@Sun.COM */
242411900SRaymond.Chen@Sun.COM static int
usbecm_get_descriptors(usbecm_state_t * ecmp)242511900SRaymond.Chen@Sun.COM usbecm_get_descriptors(usbecm_state_t *ecmp)
242611900SRaymond.Chen@Sun.COM {
242711900SRaymond.Chen@Sun.COM int i;
242811900SRaymond.Chen@Sun.COM usb_cfg_data_t *cfg;
242911900SRaymond.Chen@Sun.COM usb_alt_if_data_t *altif;
243011900SRaymond.Chen@Sun.COM usb_cvs_data_t *cvs;
243111900SRaymond.Chen@Sun.COM int16_t master_if = -1, slave_if = -1;
243211900SRaymond.Chen@Sun.COM usb_cdc_ecm_descr_t ecm_desc;
243311900SRaymond.Chen@Sun.COM usb_ep_data_t *ep_data;
243411900SRaymond.Chen@Sun.COM usb_dev_descr_t *usb_dev_desc;
243511900SRaymond.Chen@Sun.COM
243611900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
243711900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: ");
243811900SRaymond.Chen@Sun.COM
243911900SRaymond.Chen@Sun.COM usb_dev_desc = ecmp->ecm_dev_data->dev_descr;
244011900SRaymond.Chen@Sun.COM
244111900SRaymond.Chen@Sun.COM /*
244211900SRaymond.Chen@Sun.COM * Special treatment of Sun's SP Ethernet device.
244311900SRaymond.Chen@Sun.COM */
244411900SRaymond.Chen@Sun.COM if ((usb_dev_desc->idVendor == SUN_SP_VENDOR_ID) &&
244511900SRaymond.Chen@Sun.COM (usb_dev_desc->idProduct == SUN_SP_PRODUCT_ID)) {
244611900SRaymond.Chen@Sun.COM if (usb_set_cfg(ecmp->ecm_dip, ecmp->ecm_cfg_index,
244711900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) {
244811900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
244911900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: fail to set cfg ");
245011900SRaymond.Chen@Sun.COM } else {
245111900SRaymond.Chen@Sun.COM usb_free_dev_data(ecmp->ecm_dip, ecmp->ecm_dev_data);
245211900SRaymond.Chen@Sun.COM if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
245311900SRaymond.Chen@Sun.COM USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
245411900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
245511900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: fail to get"
245611900SRaymond.Chen@Sun.COM " dev_data");
245711900SRaymond.Chen@Sun.COM
245811900SRaymond.Chen@Sun.COM return (USB_FAILURE);
245911900SRaymond.Chen@Sun.COM }
246011900SRaymond.Chen@Sun.COM }
246111900SRaymond.Chen@Sun.COM }
246211900SRaymond.Chen@Sun.COM
246311900SRaymond.Chen@Sun.COM cfg = ecmp->ecm_dev_data->dev_curr_cfg;
246411900SRaymond.Chen@Sun.COM
246511900SRaymond.Chen@Sun.COM /* set default control and data interface */
246611900SRaymond.Chen@Sun.COM ecmp->ecm_ctrl_if_no = ecmp->ecm_data_if_no = 0;
246711900SRaymond.Chen@Sun.COM
246811900SRaymond.Chen@Sun.COM /* get current interfaces */
246911900SRaymond.Chen@Sun.COM ecmp->ecm_ctrl_if_no = ecmp->ecm_dev_data->dev_curr_if;
247011900SRaymond.Chen@Sun.COM if (cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt == 0) {
247111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
247211900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: elements in if_alt is %d",
247311900SRaymond.Chen@Sun.COM cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
247411900SRaymond.Chen@Sun.COM
247511900SRaymond.Chen@Sun.COM return (USB_FAILURE);
247611900SRaymond.Chen@Sun.COM }
247711900SRaymond.Chen@Sun.COM
247811900SRaymond.Chen@Sun.COM altif = &cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_alt[0];
247911900SRaymond.Chen@Sun.COM
248011900SRaymond.Chen@Sun.COM /*
248111900SRaymond.Chen@Sun.COM * Based on CDC specification, ECM devices usually include the
248211900SRaymond.Chen@Sun.COM * following function descriptors: Header, Union and ECM
248311900SRaymond.Chen@Sun.COM * Contry Selection function descriptors. This loop search tree data
248411900SRaymond.Chen@Sun.COM * structure for each ecm class descriptor.
248511900SRaymond.Chen@Sun.COM */
248611900SRaymond.Chen@Sun.COM for (i = 0; i < altif->altif_n_cvs; i++) {
248711900SRaymond.Chen@Sun.COM cvs = &altif->altif_cvs[i];
248811900SRaymond.Chen@Sun.COM
248911900SRaymond.Chen@Sun.COM if ((cvs->cvs_buf == NULL) ||
249011900SRaymond.Chen@Sun.COM (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
249111900SRaymond.Chen@Sun.COM continue;
249211900SRaymond.Chen@Sun.COM }
249311900SRaymond.Chen@Sun.COM
249411900SRaymond.Chen@Sun.COM switch (cvs->cvs_buf[2]) {
249511900SRaymond.Chen@Sun.COM case USB_CDC_DESCR_TYPE_HEADER:
249611900SRaymond.Chen@Sun.COM /*
249711900SRaymond.Chen@Sun.COM * parse header functional descriptor
249811900SRaymond.Chen@Sun.COM * Just to check integrity.
249911900SRaymond.Chen@Sun.COM */
250011900SRaymond.Chen@Sun.COM if (cvs->cvs_buf_len != 5) {
250111900SRaymond.Chen@Sun.COM return (USB_FAILURE);
250211900SRaymond.Chen@Sun.COM }
250311900SRaymond.Chen@Sun.COM break;
250411900SRaymond.Chen@Sun.COM case USB_CDC_DESCR_TYPE_ETHERNET:
250511900SRaymond.Chen@Sun.COM /* parse ECM functional descriptor */
250611900SRaymond.Chen@Sun.COM if (cvs->cvs_buf_len >= USB_CDC_ECM_LEN) {
250711900SRaymond.Chen@Sun.COM char buf[USB_MAXSTRINGLEN];
250811900SRaymond.Chen@Sun.COM
250911900SRaymond.Chen@Sun.COM if (usb_parse_data("4cl2sc", cvs->cvs_buf,
251011900SRaymond.Chen@Sun.COM cvs->cvs_buf_len, (void *)&ecm_desc,
251111900SRaymond.Chen@Sun.COM (size_t)USB_CDC_ECM_LEN) <
251211900SRaymond.Chen@Sun.COM USB_CDC_ECM_LEN) {
251311900SRaymond.Chen@Sun.COM
251411900SRaymond.Chen@Sun.COM return (USB_FAILURE);
251511900SRaymond.Chen@Sun.COM }
251611900SRaymond.Chen@Sun.COM
251711900SRaymond.Chen@Sun.COM /* get the MAC address */
251811900SRaymond.Chen@Sun.COM if (usb_get_string_descr(ecmp->ecm_dip,
251911900SRaymond.Chen@Sun.COM USB_LANG_ID, ecm_desc.iMACAddress, buf,
252011900SRaymond.Chen@Sun.COM USB_MAXSTRINGLEN) != USB_SUCCESS) {
252111900SRaymond.Chen@Sun.COM
252211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
252311900SRaymond.Chen@Sun.COM }
252411900SRaymond.Chen@Sun.COM
252511900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
252611900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: macaddr=%s ",
252711900SRaymond.Chen@Sun.COM buf);
252811900SRaymond.Chen@Sun.COM
252911900SRaymond.Chen@Sun.COM /* expects 12 characters */
253011900SRaymond.Chen@Sun.COM if (strlen(buf) < 12) {
253111900SRaymond.Chen@Sun.COM return (USB_FAILURE);
253211900SRaymond.Chen@Sun.COM }
253311900SRaymond.Chen@Sun.COM label_to_mac(buf, ecmp->ecm_srcaddr);
253411900SRaymond.Chen@Sun.COM
253511900SRaymond.Chen@Sun.COM bcopy(&ecm_desc, &ecmp->ecm_desc,
253611900SRaymond.Chen@Sun.COM USB_CDC_ECM_LEN);
253711900SRaymond.Chen@Sun.COM }
253811900SRaymond.Chen@Sun.COM break;
253911900SRaymond.Chen@Sun.COM case USB_CDC_DESCR_TYPE_UNION:
254011900SRaymond.Chen@Sun.COM /* parse Union functional descriptor. */
254111900SRaymond.Chen@Sun.COM if (cvs->cvs_buf_len >= 5) {
254211900SRaymond.Chen@Sun.COM master_if = cvs->cvs_buf[3];
254311900SRaymond.Chen@Sun.COM slave_if = cvs->cvs_buf[4];
254411900SRaymond.Chen@Sun.COM }
254511900SRaymond.Chen@Sun.COM break;
254611900SRaymond.Chen@Sun.COM default:
254711900SRaymond.Chen@Sun.COM break;
254811900SRaymond.Chen@Sun.COM }
254911900SRaymond.Chen@Sun.COM }
255011900SRaymond.Chen@Sun.COM
255111900SRaymond.Chen@Sun.COM /* For usb ecm devices, it must satisfy the following options. */
255211900SRaymond.Chen@Sun.COM if (cfg->cfg_n_if < 2) {
255311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
255411900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: # of interfaces %d < 2",
255511900SRaymond.Chen@Sun.COM cfg->cfg_n_if);
255611900SRaymond.Chen@Sun.COM
255711900SRaymond.Chen@Sun.COM return (USB_FAILURE);
255811900SRaymond.Chen@Sun.COM }
255911900SRaymond.Chen@Sun.COM
256011900SRaymond.Chen@Sun.COM if (ecmp->ecm_data_if_no == 0 &&
256111900SRaymond.Chen@Sun.COM slave_if != ecmp->ecm_data_if_no) {
256211900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
256311900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: Device has no call management "
256411900SRaymond.Chen@Sun.COM "descriptor and use Union Descriptor.");
256511900SRaymond.Chen@Sun.COM
256611900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no = slave_if;
256711900SRaymond.Chen@Sun.COM }
256811900SRaymond.Chen@Sun.COM
256911900SRaymond.Chen@Sun.COM if ((master_if != ecmp->ecm_ctrl_if_no) ||
257011900SRaymond.Chen@Sun.COM (slave_if != ecmp->ecm_data_if_no)) {
257111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
257211900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: control interface or "
257311900SRaymond.Chen@Sun.COM "data interface don't match.");
257411900SRaymond.Chen@Sun.COM
257511900SRaymond.Chen@Sun.COM return (USB_FAILURE);
257611900SRaymond.Chen@Sun.COM }
257711900SRaymond.Chen@Sun.COM
257811900SRaymond.Chen@Sun.COM if ((ecmp->ecm_ctrl_if_no >= cfg->cfg_n_if) ||
257911900SRaymond.Chen@Sun.COM (ecmp->ecm_data_if_no >= cfg->cfg_n_if)) {
258011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
258111900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: control interface %d or "
258211900SRaymond.Chen@Sun.COM "data interface %d out of range.",
258311900SRaymond.Chen@Sun.COM ecmp->ecm_ctrl_if_no, ecmp->ecm_data_if_no);
258411900SRaymond.Chen@Sun.COM
258511900SRaymond.Chen@Sun.COM return (USB_FAILURE);
258611900SRaymond.Chen@Sun.COM }
258711900SRaymond.Chen@Sun.COM
258811900SRaymond.Chen@Sun.COM /* ECM data interface has a minimal of two altsettings */
258911900SRaymond.Chen@Sun.COM if (cfg->cfg_if[ecmp->ecm_data_if_no].if_n_alt < 2) {
259011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
259111900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: elements in if_alt is %d,"
259211900SRaymond.Chen@Sun.COM " MUST >= 2", cfg->cfg_if[ecmp->ecm_ctrl_if_no].if_n_alt);
259311900SRaymond.Chen@Sun.COM
259411900SRaymond.Chen@Sun.COM return (USB_FAILURE);
259511900SRaymond.Chen@Sun.COM }
259611900SRaymond.Chen@Sun.COM
259711900SRaymond.Chen@Sun.COM /* control interface must have interrupt endpoint */
259811900SRaymond.Chen@Sun.COM if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
259911900SRaymond.Chen@Sun.COM ecmp->ecm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
260011900SRaymond.Chen@Sun.COM USB_EP_DIR_IN)) == NULL) {
260111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
260211900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: "
260311900SRaymond.Chen@Sun.COM "ctrl interface %d has no interrupt endpoint",
260411900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no);
260511900SRaymond.Chen@Sun.COM
260611900SRaymond.Chen@Sun.COM return (USB_FAILURE);
260711900SRaymond.Chen@Sun.COM }
260811900SRaymond.Chen@Sun.COM ecmp->ecm_intr_ep = ep_data;
260911900SRaymond.Chen@Sun.COM
261011900SRaymond.Chen@Sun.COM /* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */
261111900SRaymond.Chen@Sun.COM if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
261211900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
261311900SRaymond.Chen@Sun.COM USB_EP_DIR_IN)) == NULL) {
261411900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
261511900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: "
261611900SRaymond.Chen@Sun.COM "data interface %d has no bulk in endpoint",
261711900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no);
261811900SRaymond.Chen@Sun.COM
261911900SRaymond.Chen@Sun.COM return (USB_FAILURE);
262011900SRaymond.Chen@Sun.COM }
262111900SRaymond.Chen@Sun.COM ecmp->ecm_bulk_in_ep = ep_data;
262211900SRaymond.Chen@Sun.COM
262311900SRaymond.Chen@Sun.COM if ((ep_data = usb_lookup_ep_data(ecmp->ecm_dip, ecmp->ecm_dev_data,
262411900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no, 1, 0, USB_EP_ATTR_BULK,
262511900SRaymond.Chen@Sun.COM USB_EP_DIR_OUT)) == NULL) {
262611900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
262711900SRaymond.Chen@Sun.COM "usbecm_get_descriptors: "
262811900SRaymond.Chen@Sun.COM "data interface %d has no bulk out endpoint",
262911900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no);
263011900SRaymond.Chen@Sun.COM
263111900SRaymond.Chen@Sun.COM return (USB_FAILURE);
263211900SRaymond.Chen@Sun.COM }
263311900SRaymond.Chen@Sun.COM ecmp->ecm_bulk_out_ep = ep_data;
263411900SRaymond.Chen@Sun.COM
263511900SRaymond.Chen@Sun.COM /* set default value for ethernet packet filter */
263611900SRaymond.Chen@Sun.COM ecmp->ecm_pkt_flt = CDC_ECM_PKT_TYPE_DIRECTED;
263711900SRaymond.Chen@Sun.COM
263811900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
263911900SRaymond.Chen@Sun.COM }
264011900SRaymond.Chen@Sun.COM
264111900SRaymond.Chen@Sun.COM /* Generate IEEE802 style MAC address */
264211900SRaymond.Chen@Sun.COM static void
generate_ether_addr(uint8_t * mac_addr)264311900SRaymond.Chen@Sun.COM generate_ether_addr(uint8_t *mac_addr)
264411900SRaymond.Chen@Sun.COM {
2645*11902SRaymond.Chen@Sun.COM (void) random_get_bytes(mac_addr, 6);
264611900SRaymond.Chen@Sun.COM mac_addr [0] &= 0xfe; /* unicast only */
264711900SRaymond.Chen@Sun.COM mac_addr [0] |= 0x02; /* set locally administered bit */
264811900SRaymond.Chen@Sun.COM }
264911900SRaymond.Chen@Sun.COM
265011900SRaymond.Chen@Sun.COM /*
265111900SRaymond.Chen@Sun.COM * Find a pair of bulk In/Out endpoints
265211900SRaymond.Chen@Sun.COM */
usbecm_find_bulk_in_out_eps(usbecm_state_t * ecmp,uint16_t ifc,usb_if_data_t * intf)265311900SRaymond.Chen@Sun.COM int usbecm_find_bulk_in_out_eps(usbecm_state_t *ecmp,
265411900SRaymond.Chen@Sun.COM uint16_t ifc, usb_if_data_t *intf)
265511900SRaymond.Chen@Sun.COM {
265611900SRaymond.Chen@Sun.COM uint16_t alt, alt_num;
265711900SRaymond.Chen@Sun.COM usb_ep_data_t *intr_ep = NULL;
265811900SRaymond.Chen@Sun.COM usb_ep_data_t *bulk_in, *bulk_out, *ep;
265911900SRaymond.Chen@Sun.COM
266011900SRaymond.Chen@Sun.COM alt_num = intf->if_n_alt;
266111900SRaymond.Chen@Sun.COM
266211900SRaymond.Chen@Sun.COM /*
266311900SRaymond.Chen@Sun.COM * for the non-compatible devices, to make it simple, we
266411900SRaymond.Chen@Sun.COM * suppose the devices have this kind of configuration:
266511900SRaymond.Chen@Sun.COM * INTR In EP(if exists) + BULK In + Bulk Out in the
266611900SRaymond.Chen@Sun.COM * same altsetting of the same interface
266711900SRaymond.Chen@Sun.COM */
266811900SRaymond.Chen@Sun.COM for (alt = 0; alt < alt_num; alt++) {
266911900SRaymond.Chen@Sun.COM /* search pair of bulk in/out EPs */
267011900SRaymond.Chen@Sun.COM if (((bulk_in = usb_lookup_ep_data(ecmp->ecm_dip,
267111900SRaymond.Chen@Sun.COM ecmp->ecm_dev_data, ifc, alt, 0,
267211900SRaymond.Chen@Sun.COM USB_EP_ATTR_BULK,
267311900SRaymond.Chen@Sun.COM USB_EP_DIR_IN)) == NULL) ||
267411900SRaymond.Chen@Sun.COM (bulk_out = usb_lookup_ep_data(ecmp->ecm_dip,
267511900SRaymond.Chen@Sun.COM ecmp->ecm_dev_data, ifc, alt, 0,
267611900SRaymond.Chen@Sun.COM USB_EP_ATTR_BULK,
267711900SRaymond.Chen@Sun.COM USB_EP_DIR_OUT)) == NULL) {
267811900SRaymond.Chen@Sun.COM
267911900SRaymond.Chen@Sun.COM continue;
268011900SRaymond.Chen@Sun.COM }
268111900SRaymond.Chen@Sun.COM
268211900SRaymond.Chen@Sun.COM /*
268311900SRaymond.Chen@Sun.COM * search interrupt pipe.
268411900SRaymond.Chen@Sun.COM */
268511900SRaymond.Chen@Sun.COM if ((ep = usb_lookup_ep_data(ecmp->ecm_dip,
268611900SRaymond.Chen@Sun.COM ecmp->ecm_dev_data, ifc, alt, 0,
268711900SRaymond.Chen@Sun.COM USB_EP_ATTR_INTR, USB_EP_DIR_IN)) != NULL) {
268811900SRaymond.Chen@Sun.COM intr_ep = ep;
268911900SRaymond.Chen@Sun.COM }
269011900SRaymond.Chen@Sun.COM
269111900SRaymond.Chen@Sun.COM
269211900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no = ifc;
269311900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_alt = alt;
269411900SRaymond.Chen@Sun.COM ecmp->ecm_intr_ep = intr_ep;
269511900SRaymond.Chen@Sun.COM ecmp->ecm_ctrl_if_no = ifc;
269611900SRaymond.Chen@Sun.COM ecmp->ecm_bulk_in_ep = bulk_in;
269711900SRaymond.Chen@Sun.COM ecmp->ecm_bulk_out_ep = bulk_out;
269811900SRaymond.Chen@Sun.COM
269911900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
270011900SRaymond.Chen@Sun.COM }
270111900SRaymond.Chen@Sun.COM
270211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
270311900SRaymond.Chen@Sun.COM }
270411900SRaymond.Chen@Sun.COM
270511900SRaymond.Chen@Sun.COM static int
usbecm_init_non_compatible_device(usbecm_state_t * ecmp)270611900SRaymond.Chen@Sun.COM usbecm_init_non_compatible_device(usbecm_state_t *ecmp)
270711900SRaymond.Chen@Sun.COM {
270811900SRaymond.Chen@Sun.COM usb_if_data_t *cur_if;
270911900SRaymond.Chen@Sun.COM uint16_t if_num, i;
271011900SRaymond.Chen@Sun.COM
271111900SRaymond.Chen@Sun.COM /*
271211900SRaymond.Chen@Sun.COM * If device don't conform to spec, search pairs of bulk in/out
271311900SRaymond.Chen@Sun.COM * endpoints and fill related structure. We suppose this driver
271411900SRaymond.Chen@Sun.COM * is bound to a interface.
271511900SRaymond.Chen@Sun.COM */
271611900SRaymond.Chen@Sun.COM cur_if = ecmp->ecm_dev_data->dev_curr_cfg->cfg_if;
271711900SRaymond.Chen@Sun.COM if_num = ecmp->ecm_dev_data->dev_curr_cfg->cfg_n_if;
271811900SRaymond.Chen@Sun.COM
271911900SRaymond.Chen@Sun.COM /* search each interface which have bulk in and out */
272011900SRaymond.Chen@Sun.COM for (i = 0; i < if_num; i++) {
272111900SRaymond.Chen@Sun.COM if (usbecm_find_bulk_in_out_eps(ecmp, i,
272211900SRaymond.Chen@Sun.COM cur_if) == USB_SUCCESS) {
272311900SRaymond.Chen@Sun.COM
272411900SRaymond.Chen@Sun.COM break;
272511900SRaymond.Chen@Sun.COM }
272611900SRaymond.Chen@Sun.COM cur_if++;
272711900SRaymond.Chen@Sun.COM }
272811900SRaymond.Chen@Sun.COM
272911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ATTA, ecmp->ecm_lh,
273011900SRaymond.Chen@Sun.COM "usbecm_init_non_compatible_device: ctrl_if=%d,"
273111900SRaymond.Chen@Sun.COM " data_if=%d, alt=%d", ecmp->ecm_ctrl_if_no,
273211900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no, ecmp->ecm_data_if_alt);
273311900SRaymond.Chen@Sun.COM
273411900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
273511900SRaymond.Chen@Sun.COM }
273611900SRaymond.Chen@Sun.COM
273711900SRaymond.Chen@Sun.COM static boolean_t
usbecm_is_compatible(usbecm_state_t * ecmp)273811900SRaymond.Chen@Sun.COM usbecm_is_compatible(usbecm_state_t *ecmp)
273911900SRaymond.Chen@Sun.COM {
274011900SRaymond.Chen@Sun.COM usb_cfg_data_t *cfg_data;
274111900SRaymond.Chen@Sun.COM usb_if_data_t *intf;
274211900SRaymond.Chen@Sun.COM usb_alt_if_data_t *alt;
274311900SRaymond.Chen@Sun.COM int alt_num, if_num, cfg_num;
274411900SRaymond.Chen@Sun.COM int i, j, cfg_index;
274511900SRaymond.Chen@Sun.COM
274611900SRaymond.Chen@Sun.COM cfg_num = ecmp->ecm_dev_data->dev_n_cfg;
274711900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
274811900SRaymond.Chen@Sun.COM "usbecm_is_compatible: entry, cfg_num=%d", cfg_num);
274911900SRaymond.Chen@Sun.COM
275011900SRaymond.Chen@Sun.COM for (cfg_index = 0; cfg_index < cfg_num; cfg_index++) {
275111900SRaymond.Chen@Sun.COM cfg_data = &(ecmp->ecm_dev_data->dev_cfg[cfg_index]);
275211900SRaymond.Chen@Sun.COM
275311900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
275411900SRaymond.Chen@Sun.COM "usbecm_is_compatible: cfg_index=%d, value=%d",
275511900SRaymond.Chen@Sun.COM cfg_index, cfg_data->cfg_descr.bConfigurationValue);
275611900SRaymond.Chen@Sun.COM
275711900SRaymond.Chen@Sun.COM intf = cfg_data->cfg_if;
275811900SRaymond.Chen@Sun.COM if_num = cfg_data->cfg_n_if;
275911900SRaymond.Chen@Sun.COM
276011900SRaymond.Chen@Sun.COM for (i = 0; i < if_num; i++) {
276111900SRaymond.Chen@Sun.COM alt_num = intf->if_n_alt;
276211900SRaymond.Chen@Sun.COM for (j = 0; j < alt_num; j++) {
276311900SRaymond.Chen@Sun.COM alt = &intf->if_alt[j];
276411900SRaymond.Chen@Sun.COM if ((alt->altif_descr.bInterfaceClass == 0x02) &&
276511900SRaymond.Chen@Sun.COM (alt->altif_descr.bInterfaceSubClass == 0x06)) {
276611900SRaymond.Chen@Sun.COM ecmp->ecm_cfg_index = cfg_index;
276711900SRaymond.Chen@Sun.COM
276811900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_ATTA, ecmp->ecm_lh,
276911900SRaymond.Chen@Sun.COM "usbecm_is_compatible: cfg_index=%d",
277011900SRaymond.Chen@Sun.COM cfg_index);
277111900SRaymond.Chen@Sun.COM
277211900SRaymond.Chen@Sun.COM return (B_TRUE);
277311900SRaymond.Chen@Sun.COM }
277411900SRaymond.Chen@Sun.COM }
277511900SRaymond.Chen@Sun.COM intf++;
277611900SRaymond.Chen@Sun.COM }
277711900SRaymond.Chen@Sun.COM }
277811900SRaymond.Chen@Sun.COM
277911900SRaymond.Chen@Sun.COM return (B_FALSE);
278011900SRaymond.Chen@Sun.COM }
278111900SRaymond.Chen@Sun.COM
278211900SRaymond.Chen@Sun.COM
278311900SRaymond.Chen@Sun.COM static int
usbecm_usb_init(usbecm_state_t * ecmp)278411900SRaymond.Chen@Sun.COM usbecm_usb_init(usbecm_state_t *ecmp)
278511900SRaymond.Chen@Sun.COM {
278611900SRaymond.Chen@Sun.COM
278711900SRaymond.Chen@Sun.COM if (usb_client_attach(ecmp->ecm_dip, USBDRV_VERSION, 0) !=
278811900SRaymond.Chen@Sun.COM USB_SUCCESS) {
278911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
279011900SRaymond.Chen@Sun.COM "usbecm_usb_init: fail to attach");
279111900SRaymond.Chen@Sun.COM
279211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
279311900SRaymond.Chen@Sun.COM }
279411900SRaymond.Chen@Sun.COM
279511900SRaymond.Chen@Sun.COM /* Get the configuration information of device */
279611900SRaymond.Chen@Sun.COM if (usb_get_dev_data(ecmp->ecm_dip, &ecmp->ecm_dev_data,
279711900SRaymond.Chen@Sun.COM USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
279811900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
279911900SRaymond.Chen@Sun.COM "usbecm_usb_init: fail to get_dev_data");
280011900SRaymond.Chen@Sun.COM
280111900SRaymond.Chen@Sun.COM return (USB_FAILURE);
280211900SRaymond.Chen@Sun.COM }
280311900SRaymond.Chen@Sun.COM ecmp->ecm_def_ph = ecmp->ecm_dev_data->dev_default_ph;
280411900SRaymond.Chen@Sun.COM ecmp->ecm_dev_state = USB_DEV_ONLINE;
280511900SRaymond.Chen@Sun.COM
280611900SRaymond.Chen@Sun.COM mutex_init(&ecmp->ecm_mutex, NULL, MUTEX_DRIVER,
280711900SRaymond.Chen@Sun.COM ecmp->ecm_dev_data->dev_iblock_cookie);
280811900SRaymond.Chen@Sun.COM
280911900SRaymond.Chen@Sun.COM if ((strcmp(ddi_binding_name(ecmp->ecm_dip),
281011900SRaymond.Chen@Sun.COM "usbif,class2.6") == 0) ||
281111900SRaymond.Chen@Sun.COM ((strcmp(ddi_binding_name(ecmp->ecm_dip),
281211900SRaymond.Chen@Sun.COM "usb,class2.6.0") == 0))) {
281311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
281411900SRaymond.Chen@Sun.COM "usbecm_usb_init: A CDC ECM device is attached");
281511900SRaymond.Chen@Sun.COM ecmp->ecm_compatibility = B_TRUE;
281611900SRaymond.Chen@Sun.COM } else if (usb_owns_device(ecmp->ecm_dip) &&
281711900SRaymond.Chen@Sun.COM usbecm_is_compatible(ecmp)) {
281811900SRaymond.Chen@Sun.COM /*
281911900SRaymond.Chen@Sun.COM * Current Sun SP ECM device has two configurations. Hence
282011900SRaymond.Chen@Sun.COM * USBA doesn't create interface level compatible names
282111900SRaymond.Chen@Sun.COM * for it, see usba_ready_device_node(). We have to check
282211900SRaymond.Chen@Sun.COM * manually to see if compatible interfaces exist, when
282311900SRaymond.Chen@Sun.COM * the driver owns the entire device.
282411900SRaymond.Chen@Sun.COM */
282511900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
282611900SRaymond.Chen@Sun.COM "usbecm_usb_init: A CDC ECM device is attached");
282711900SRaymond.Chen@Sun.COM ecmp->ecm_compatibility = B_TRUE;
282811900SRaymond.Chen@Sun.COM } else {
282911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
283011900SRaymond.Chen@Sun.COM "usbecm_usb_init: A nonstandard device is attached to "
283111900SRaymond.Chen@Sun.COM "usbecm(7D) driver. This device doesn't conform to "
283211900SRaymond.Chen@Sun.COM "usb cdc spec.");
283311900SRaymond.Chen@Sun.COM ecmp->ecm_compatibility = B_FALSE;
283411900SRaymond.Chen@Sun.COM
283511900SRaymond.Chen@Sun.COM /* generate a random MAC addr */
283611900SRaymond.Chen@Sun.COM generate_ether_addr(ecmp->ecm_srcaddr);
283711900SRaymond.Chen@Sun.COM }
283811900SRaymond.Chen@Sun.COM
283911900SRaymond.Chen@Sun.COM if ((ecmp->ecm_compatibility == B_TRUE) &&
284011900SRaymond.Chen@Sun.COM (usbecm_get_descriptors(ecmp) != USB_SUCCESS)) {
284111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
284211900SRaymond.Chen@Sun.COM "usbecm_usb_init: A compatible device is attached, but "
284311900SRaymond.Chen@Sun.COM "fail to get standard descriptors");
284411900SRaymond.Chen@Sun.COM
284511900SRaymond.Chen@Sun.COM return (USB_FAILURE);
284611900SRaymond.Chen@Sun.COM }
284711900SRaymond.Chen@Sun.COM
284811900SRaymond.Chen@Sun.COM if (ecmp->ecm_compatibility == B_FALSE) {
284911900SRaymond.Chen@Sun.COM (void) usbecm_init_non_compatible_device(ecmp);
285011900SRaymond.Chen@Sun.COM }
285111900SRaymond.Chen@Sun.COM
285211900SRaymond.Chen@Sun.COM /* Create power management components */
285311900SRaymond.Chen@Sun.COM if (usbecm_create_pm_components(ecmp) != USB_SUCCESS) {
285411900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
285511900SRaymond.Chen@Sun.COM "usbecm_usb_init: create pm components failed.");
285611900SRaymond.Chen@Sun.COM
285711900SRaymond.Chen@Sun.COM return (USB_FAILURE);
285811900SRaymond.Chen@Sun.COM }
285911900SRaymond.Chen@Sun.COM
286011900SRaymond.Chen@Sun.COM /* Register to get callbacks for USB events */
286111900SRaymond.Chen@Sun.COM if (usb_register_event_cbs(ecmp->ecm_dip, &usbecm_events, 0)
286211900SRaymond.Chen@Sun.COM != USB_SUCCESS) {
286311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
286411900SRaymond.Chen@Sun.COM "usbsecm_attach: register event callback failed.");
286511900SRaymond.Chen@Sun.COM
286611900SRaymond.Chen@Sun.COM return (USB_FAILURE);
286711900SRaymond.Chen@Sun.COM }
286811900SRaymond.Chen@Sun.COM ecmp->ecm_init_flags |= USBECM_INIT_EVENTS;
286911900SRaymond.Chen@Sun.COM
287011900SRaymond.Chen@Sun.COM
287111900SRaymond.Chen@Sun.COM /* Get max data size of bulk transfer */
287211900SRaymond.Chen@Sun.COM if (usb_pipe_get_max_bulk_transfer_size(ecmp->ecm_dip,
287311900SRaymond.Chen@Sun.COM &ecmp->ecm_xfer_sz) != USB_SUCCESS) {
287411900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
287511900SRaymond.Chen@Sun.COM "usbsecm_ds_attach: get max size of transfer failed.");
287611900SRaymond.Chen@Sun.COM
287711900SRaymond.Chen@Sun.COM return (USB_FAILURE);
287811900SRaymond.Chen@Sun.COM }
287911900SRaymond.Chen@Sun.COM
288011900SRaymond.Chen@Sun.COM
288111900SRaymond.Chen@Sun.COM ecmp->ecm_ser_acc = usb_init_serialization(ecmp->ecm_dip,
288211900SRaymond.Chen@Sun.COM USB_INIT_SER_CHECK_SAME_THREAD);
288311900SRaymond.Chen@Sun.COM ecmp->ecm_init_flags |= USBECM_INIT_SER;
288411900SRaymond.Chen@Sun.COM
288511900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
288611900SRaymond.Chen@Sun.COM }
288711900SRaymond.Chen@Sun.COM
288811900SRaymond.Chen@Sun.COM
288911900SRaymond.Chen@Sun.COM /*
289011900SRaymond.Chen@Sun.COM * Open operation pipes. Each ECM device should have Bulk In, Bulk Out
289111900SRaymond.Chen@Sun.COM * and Interrupt In endpoints
289211900SRaymond.Chen@Sun.COM */
289311900SRaymond.Chen@Sun.COM static int
usbecm_open_pipes(usbecm_state_t * ecmp)289411900SRaymond.Chen@Sun.COM usbecm_open_pipes(usbecm_state_t *ecmp)
289511900SRaymond.Chen@Sun.COM {
289611900SRaymond.Chen@Sun.COM int rval = USB_SUCCESS;
289711900SRaymond.Chen@Sun.COM usb_ep_data_t *in_data, *out_data, *intr_pipe;
289811900SRaymond.Chen@Sun.COM usb_pipe_policy_t policy;
289911900SRaymond.Chen@Sun.COM int altif;
290011900SRaymond.Chen@Sun.COM
290111900SRaymond.Chen@Sun.COM ASSERT(!mutex_owned(&ecmp->ecm_mutex));
290211900SRaymond.Chen@Sun.COM
290311900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
290411900SRaymond.Chen@Sun.COM "usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp);
290511900SRaymond.Chen@Sun.COM
290611900SRaymond.Chen@Sun.COM if (ecmp->ecm_compatibility == B_TRUE) {
290711900SRaymond.Chen@Sun.COM /* compatible device has minimum of 2 altsetting, select alt 1 */
290811900SRaymond.Chen@Sun.COM altif = 1;
290911900SRaymond.Chen@Sun.COM } else {
291011900SRaymond.Chen@Sun.COM altif = ecmp->ecm_data_if_alt;
291111900SRaymond.Chen@Sun.COM }
291211900SRaymond.Chen@Sun.COM intr_pipe = ecmp->ecm_intr_ep;
291311900SRaymond.Chen@Sun.COM in_data = ecmp->ecm_bulk_in_ep;
291411900SRaymond.Chen@Sun.COM out_data = ecmp->ecm_bulk_out_ep;
291511900SRaymond.Chen@Sun.COM
291611900SRaymond.Chen@Sun.COM /* Bulk in and out must exist simultaneously. */
291711900SRaymond.Chen@Sun.COM if ((in_data == NULL) || (out_data == NULL)) {
291811900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
291911900SRaymond.Chen@Sun.COM "usbsecm_open_pipes: look up bulk pipe failed in "
292011900SRaymond.Chen@Sun.COM "interface %d ",
292111900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no);
292211900SRaymond.Chen@Sun.COM
292311900SRaymond.Chen@Sun.COM return (USB_FAILURE);
292411900SRaymond.Chen@Sun.COM }
292511900SRaymond.Chen@Sun.COM /*
292611900SRaymond.Chen@Sun.COM * If device conform to ecm spec, it must have an interrupt pipe
292711900SRaymond.Chen@Sun.COM * for this device.
292811900SRaymond.Chen@Sun.COM */
292911900SRaymond.Chen@Sun.COM if (ecmp->ecm_compatibility == B_TRUE && intr_pipe == NULL) {
293011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
293111900SRaymond.Chen@Sun.COM "usbecm_open_pipes: look up interrupt pipe failed in "
293211900SRaymond.Chen@Sun.COM "interface %d", ecmp->ecm_ctrl_if_no);
293311900SRaymond.Chen@Sun.COM
293411900SRaymond.Chen@Sun.COM return (USB_FAILURE);
293511900SRaymond.Chen@Sun.COM }
293611900SRaymond.Chen@Sun.COM
293711900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
293811900SRaymond.Chen@Sun.COM "usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x",
293911900SRaymond.Chen@Sun.COM intr_pipe?intr_pipe->ep_descr.bEndpointAddress:0,
294011900SRaymond.Chen@Sun.COM in_data->ep_descr.bEndpointAddress,
294111900SRaymond.Chen@Sun.COM out_data->ep_descr.bEndpointAddress);
294211900SRaymond.Chen@Sun.COM
294311900SRaymond.Chen@Sun.COM USB_DPRINTF_L3(PRINT_MASK_OPEN, ecmp->ecm_lh,
294411900SRaymond.Chen@Sun.COM "usbsecm_open_pipes: set data if(%d) alt(%d) ",
294511900SRaymond.Chen@Sun.COM ecmp->ecm_data_if_no, altif);
294611900SRaymond.Chen@Sun.COM
294711900SRaymond.Chen@Sun.COM if ((rval = usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
294811900SRaymond.Chen@Sun.COM altif, USB_FLAGS_SLEEP, NULL, NULL)) != USB_SUCCESS) {
294911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
295011900SRaymond.Chen@Sun.COM "usbecm_open_pipes: set alternate failed (%d)",
295111900SRaymond.Chen@Sun.COM rval);
295211900SRaymond.Chen@Sun.COM
295311900SRaymond.Chen@Sun.COM return (rval);
295411900SRaymond.Chen@Sun.COM }
295511900SRaymond.Chen@Sun.COM
295611900SRaymond.Chen@Sun.COM policy.pp_max_async_reqs = 2;
295711900SRaymond.Chen@Sun.COM
295811900SRaymond.Chen@Sun.COM /* Open bulk in endpoint */
295911900SRaymond.Chen@Sun.COM if (usb_pipe_open(ecmp->ecm_dip, &in_data->ep_descr, &policy,
296011900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, &ecmp->ecm_bulkin_ph) != USB_SUCCESS) {
296111900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
296211900SRaymond.Chen@Sun.COM "usbecm_open_pipes: open bulkin pipe failed!");
296311900SRaymond.Chen@Sun.COM
296411900SRaymond.Chen@Sun.COM return (USB_FAILURE);
296511900SRaymond.Chen@Sun.COM }
296611900SRaymond.Chen@Sun.COM
296711900SRaymond.Chen@Sun.COM /* Open bulk out endpoint */
296811900SRaymond.Chen@Sun.COM if (usb_pipe_open(ecmp->ecm_dip, &out_data->ep_descr, &policy,
296911900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, &ecmp->ecm_bulkout_ph) != USB_SUCCESS) {
297011900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
297111900SRaymond.Chen@Sun.COM "usbecm_open_pipes: open bulkout pipe failed!");
297211900SRaymond.Chen@Sun.COM
297311900SRaymond.Chen@Sun.COM usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
297411900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, NULL);
297511900SRaymond.Chen@Sun.COM
297611900SRaymond.Chen@Sun.COM return (USB_FAILURE);
297711900SRaymond.Chen@Sun.COM }
297811900SRaymond.Chen@Sun.COM
297911900SRaymond.Chen@Sun.COM /* Open interrupt endpoint if found. */
298011900SRaymond.Chen@Sun.COM if (intr_pipe != NULL) {
298111900SRaymond.Chen@Sun.COM if (usb_pipe_open(ecmp->ecm_dip, &intr_pipe->ep_descr, &policy,
298211900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, &ecmp->ecm_intr_ph) != USB_SUCCESS) {
298311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_OPEN, ecmp->ecm_lh,
298411900SRaymond.Chen@Sun.COM "usbecm_open_pipes: "
298511900SRaymond.Chen@Sun.COM "open intr pipe failed");
298611900SRaymond.Chen@Sun.COM
298711900SRaymond.Chen@Sun.COM usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
298811900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, NULL);
298911900SRaymond.Chen@Sun.COM usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
299011900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, NULL);
299111900SRaymond.Chen@Sun.COM
299211900SRaymond.Chen@Sun.COM return (USB_FAILURE);
299311900SRaymond.Chen@Sun.COM }
299411900SRaymond.Chen@Sun.COM }
299511900SRaymond.Chen@Sun.COM
299611900SRaymond.Chen@Sun.COM /* initialize the pipe related data */
299711900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
299811900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_sz = in_data->ep_descr.wMaxPacketSize;
299911900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_state = USBECM_PIPE_IDLE;
300011900SRaymond.Chen@Sun.COM ecmp->ecm_bulkout_state = USBECM_PIPE_IDLE;
300111900SRaymond.Chen@Sun.COM if (ecmp->ecm_intr_ph != NULL) {
300211900SRaymond.Chen@Sun.COM ecmp->ecm_intr_state = USBECM_PIPE_IDLE;
300311900SRaymond.Chen@Sun.COM }
300411900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
300511900SRaymond.Chen@Sun.COM
300611900SRaymond.Chen@Sun.COM if (ecmp->ecm_intr_ph != NULL) {
300711900SRaymond.Chen@Sun.COM
300811900SRaymond.Chen@Sun.COM usbecm_pipe_start_polling(ecmp);
300911900SRaymond.Chen@Sun.COM }
301011900SRaymond.Chen@Sun.COM
301111900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_OPEN, ecmp->ecm_lh,
301211900SRaymond.Chen@Sun.COM "usbsecm_open_pipes: end");
301311900SRaymond.Chen@Sun.COM
301411900SRaymond.Chen@Sun.COM return (rval);
301511900SRaymond.Chen@Sun.COM }
301611900SRaymond.Chen@Sun.COM
301711900SRaymond.Chen@Sun.COM
301811900SRaymond.Chen@Sun.COM /*
301911900SRaymond.Chen@Sun.COM * usbsecm_close_pipes:
302011900SRaymond.Chen@Sun.COM * Close pipes
302111900SRaymond.Chen@Sun.COM * Each device could include three pipes: bulk in, bulk out and interrupt.
302211900SRaymond.Chen@Sun.COM */
302311900SRaymond.Chen@Sun.COM static void
usbecm_close_pipes(usbecm_state_t * ecmp)302411900SRaymond.Chen@Sun.COM usbecm_close_pipes(usbecm_state_t *ecmp)
302511900SRaymond.Chen@Sun.COM {
302611900SRaymond.Chen@Sun.COM
302711900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
302811900SRaymond.Chen@Sun.COM
302911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
303011900SRaymond.Chen@Sun.COM "usbsecm_close_pipes: ecm_bulkin_state = %d",
303111900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_state);
303211900SRaymond.Chen@Sun.COM
303311900SRaymond.Chen@Sun.COM /*
303411900SRaymond.Chen@Sun.COM * Check the status of the pipes. If pipe is closing or closed,
303511900SRaymond.Chen@Sun.COM * return directly.
303611900SRaymond.Chen@Sun.COM */
303711900SRaymond.Chen@Sun.COM if ((ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSED) ||
303811900SRaymond.Chen@Sun.COM (ecmp->ecm_bulkin_state == USBECM_PIPE_CLOSING)) {
303911900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_CLOSE, ecmp->ecm_lh,
304011900SRaymond.Chen@Sun.COM "usbsecm_close_pipes: pipe is closing or has closed");
304111900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
304211900SRaymond.Chen@Sun.COM
304311900SRaymond.Chen@Sun.COM return;
304411900SRaymond.Chen@Sun.COM }
304511900SRaymond.Chen@Sun.COM
304611900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSING;
304711900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
304811900SRaymond.Chen@Sun.COM
304911900SRaymond.Chen@Sun.COM /* reset the data interface's altsetting to 0 */
305011900SRaymond.Chen@Sun.COM if ((ecmp->ecm_dev_state == USB_DEV_ONLINE) &&
305111900SRaymond.Chen@Sun.COM (usb_set_alt_if(ecmp->ecm_dip, ecmp->ecm_data_if_no,
305211900SRaymond.Chen@Sun.COM 0, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS)) {
305311900SRaymond.Chen@Sun.COM USB_DPRINTF_L2(PRINT_MASK_ATTA, ecmp->ecm_lh,
305411900SRaymond.Chen@Sun.COM "usbecm_close_pipes: reset alternate failed ");
305511900SRaymond.Chen@Sun.COM }
305611900SRaymond.Chen@Sun.COM
305711900SRaymond.Chen@Sun.COM /* Close pipes */
305811900SRaymond.Chen@Sun.COM usb_pipe_reset(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
305911900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, 0);
306011900SRaymond.Chen@Sun.COM usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkin_ph,
306111900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, 0);
306211900SRaymond.Chen@Sun.COM usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_bulkout_ph,
306311900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, 0);
306411900SRaymond.Chen@Sun.COM
306511900SRaymond.Chen@Sun.COM if (ecmp->ecm_intr_ph != NULL) {
306611900SRaymond.Chen@Sun.COM usb_pipe_stop_intr_polling(ecmp->ecm_intr_ph,
306711900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP);
306811900SRaymond.Chen@Sun.COM usb_pipe_close(ecmp->ecm_dip, ecmp->ecm_intr_ph,
306911900SRaymond.Chen@Sun.COM USB_FLAGS_SLEEP, NULL, 0);
307011900SRaymond.Chen@Sun.COM }
307111900SRaymond.Chen@Sun.COM
307211900SRaymond.Chen@Sun.COM mutex_enter(&ecmp->ecm_mutex);
307311900SRaymond.Chen@Sun.COM /* Reset the status of pipes to closed */
307411900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_state = USBECM_PIPE_CLOSED;
307511900SRaymond.Chen@Sun.COM ecmp->ecm_bulkin_ph = NULL;
307611900SRaymond.Chen@Sun.COM ecmp->ecm_bulkout_state = USBECM_PIPE_CLOSED;
307711900SRaymond.Chen@Sun.COM ecmp->ecm_bulkout_ph = NULL;
307811900SRaymond.Chen@Sun.COM if (ecmp->ecm_intr_ph != NULL) {
307911900SRaymond.Chen@Sun.COM ecmp->ecm_intr_state = USBECM_PIPE_CLOSED;
308011900SRaymond.Chen@Sun.COM ecmp->ecm_intr_ph = NULL;
308111900SRaymond.Chen@Sun.COM }
308211900SRaymond.Chen@Sun.COM
308311900SRaymond.Chen@Sun.COM mutex_exit(&ecmp->ecm_mutex);
308411900SRaymond.Chen@Sun.COM
308511900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_CLOSE, ecmp->ecm_lh,
308611900SRaymond.Chen@Sun.COM "usbsecm_close_pipes: pipes have been closed.");
308711900SRaymond.Chen@Sun.COM }
308811900SRaymond.Chen@Sun.COM
308911900SRaymond.Chen@Sun.COM
309011900SRaymond.Chen@Sun.COM static int
usbecm_ctrl_write(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data)309111900SRaymond.Chen@Sun.COM usbecm_ctrl_write(usbecm_state_t *ecmp, uchar_t request,
309211900SRaymond.Chen@Sun.COM uint16_t value, mblk_t **data)
309311900SRaymond.Chen@Sun.COM {
309411900SRaymond.Chen@Sun.COM usb_ctrl_setup_t setup;
309511900SRaymond.Chen@Sun.COM usb_cb_flags_t cb_flags;
309611900SRaymond.Chen@Sun.COM usb_cr_t cr;
309711900SRaymond.Chen@Sun.COM int rval;
309811900SRaymond.Chen@Sun.COM
309911900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
310011900SRaymond.Chen@Sun.COM "usbecm_ctrl_write: ");
310111900SRaymond.Chen@Sun.COM
310211900SRaymond.Chen@Sun.COM /* initialize the control request. */
310311900SRaymond.Chen@Sun.COM setup.bmRequestType = USB_DEV_REQ_HOST_TO_DEV |
310411900SRaymond.Chen@Sun.COM USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
310511900SRaymond.Chen@Sun.COM setup.bRequest = request;
310611900SRaymond.Chen@Sun.COM setup.wValue = value;
310711900SRaymond.Chen@Sun.COM setup.wIndex = ecmp->ecm_ctrl_if_no;
310811900SRaymond.Chen@Sun.COM setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
310911900SRaymond.Chen@Sun.COM setup.attrs = 0;
311011900SRaymond.Chen@Sun.COM
311111900SRaymond.Chen@Sun.COM rval = usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
311211900SRaymond.Chen@Sun.COM &cr, &cb_flags, 0);
311311900SRaymond.Chen@Sun.COM
311411900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
311511900SRaymond.Chen@Sun.COM "usbecm_ctrl_write: rval = %d", rval);
311611900SRaymond.Chen@Sun.COM
311711900SRaymond.Chen@Sun.COM return (rval);
311811900SRaymond.Chen@Sun.COM }
311911900SRaymond.Chen@Sun.COM
312011900SRaymond.Chen@Sun.COM static int
usbecm_ctrl_read(usbecm_state_t * ecmp,uchar_t request,uint16_t value,mblk_t ** data,int len)312111900SRaymond.Chen@Sun.COM usbecm_ctrl_read(usbecm_state_t *ecmp, uchar_t request,
312211900SRaymond.Chen@Sun.COM uint16_t value, mblk_t **data, int len)
312311900SRaymond.Chen@Sun.COM {
312411900SRaymond.Chen@Sun.COM usb_ctrl_setup_t setup;
312511900SRaymond.Chen@Sun.COM usb_cb_flags_t cb_flags;
312611900SRaymond.Chen@Sun.COM usb_cr_t cr;
312711900SRaymond.Chen@Sun.COM
312811900SRaymond.Chen@Sun.COM USB_DPRINTF_L4(PRINT_MASK_ALL, ecmp->ecm_lh,
312911900SRaymond.Chen@Sun.COM "usbecm_ctrl_read: ");
313011900SRaymond.Chen@Sun.COM
313111900SRaymond.Chen@Sun.COM /* initialize the control request. */
313211900SRaymond.Chen@Sun.COM setup.bmRequestType = USB_DEV_REQ_DEV_TO_HOST |
313311900SRaymond.Chen@Sun.COM USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_RCPT_IF;
313411900SRaymond.Chen@Sun.COM setup.bRequest = request;
313511900SRaymond.Chen@Sun.COM setup.wValue = value;
313611900SRaymond.Chen@Sun.COM setup.wIndex = ecmp->ecm_ctrl_if_no;
313711900SRaymond.Chen@Sun.COM setup.wLength = (uint16_t)len;
313811900SRaymond.Chen@Sun.COM setup.attrs = 0;
313911900SRaymond.Chen@Sun.COM
314011900SRaymond.Chen@Sun.COM return (usb_pipe_ctrl_xfer_wait(ecmp->ecm_def_ph, &setup, data,
314111900SRaymond.Chen@Sun.COM &cr, &cb_flags, 0));
314211900SRaymond.Chen@Sun.COM }
314311900SRaymond.Chen@Sun.COM
314411900SRaymond.Chen@Sun.COM /* Get specific statistic data from device */
314511900SRaymond.Chen@Sun.COM static int
usbecm_get_statistics(usbecm_state_t * ecmp,uint32_t fs,uint32_t * stat_data)314611900SRaymond.Chen@Sun.COM usbecm_get_statistics(usbecm_state_t *ecmp, uint32_t fs, uint32_t *stat_data)
314711900SRaymond.Chen@Sun.COM {
314811900SRaymond.Chen@Sun.COM mblk_t *data = NULL;
314911900SRaymond.Chen@Sun.COM uint32_t stat;
315011900SRaymond.Chen@Sun.COM
315111900SRaymond.Chen@Sun.COM /* first check to see if this stat is collected by device */
315211900SRaymond.Chen@Sun.COM if ((ecmp->ecm_compatibility == B_TRUE) &&
315311900SRaymond.Chen@Sun.COM (ecmp->ecm_desc.bmEthernetStatistics & ECM_STAT_CAP_MASK(fs))) {
315411900SRaymond.Chen@Sun.COM if (usbecm_ctrl_read(ecmp, CDC_ECM_GET_ETH_STAT,
315511900SRaymond.Chen@Sun.COM ecmp->ecm_ctrl_if_no, &data, 4) != USB_SUCCESS) {
315611900SRaymond.Chen@Sun.COM
315711900SRaymond.Chen@Sun.COM return (USB_FAILURE);
315811900SRaymond.Chen@Sun.COM }
315911900SRaymond.Chen@Sun.COM stat = (data->b_rptr[3] << 24) | (data->b_rptr[2] << 16) |
316011900SRaymond.Chen@Sun.COM (data->b_rptr[1] << 8) | (data->b_rptr[0]);
316111900SRaymond.Chen@Sun.COM *stat_data = stat;
316211900SRaymond.Chen@Sun.COM
316311900SRaymond.Chen@Sun.COM freemsg(data);
316411900SRaymond.Chen@Sun.COM
316511900SRaymond.Chen@Sun.COM return (USB_SUCCESS);
316611900SRaymond.Chen@Sun.COM }
316711900SRaymond.Chen@Sun.COM
316811900SRaymond.Chen@Sun.COM return (USB_FAILURE);
316911900SRaymond.Chen@Sun.COM }
3170