/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* USB Ethernet Control Model
*
* USB-IF defines three ethernet network related specifications: EEM,
* ECM and NCM. This driver focuses specifically on ECM compatible
* devices. This kind of devices generally have one pair of bulk
* device notification.
*
* Devices which don't report ECM compatibility through descriptors but
* implement the ECM functions may also bind to this driver. This driver
* will try to find at least a bulk in endpoint and a bulk out endpoint
* in this case. If the non-compatible devices use vendor specific data
* format, this driver will not function.
*
* This driver is a normal USBA client driver. It's also a GLDv3 driver,
* which provides the necessary interfaces the GLDv3 framework requires.
*
*/
#include <sys/byteorder.h>
#include <sys/mac_provider.h>
#include <sys/ethernet.h>
/* MAC callbacks */
static int usbecm_m_start(void *arg);
static void usbecm_m_stop(void *arg);
/* utils */
/* Driver identification */
/* Global state pointer for managing per-device soft states */
void *usbecm_statep;
/* print levels */
/*
* to prevent upper layers packet flood from exhausting system
* resources(USBA does not set limitation of requests on a pipe),
* we set a upper limit for the transfer queue length.
*/
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
};
/*
* MAC Call Back entries
*/
usbecm_m_stat, /* Get the value of a statistic */
usbecm_m_start, /* Start the device */
usbecm_m_stop, /* Stop the device */
usbecm_m_promisc, /* Enable or disable promiscuous mode */
usbecm_m_multicst, /* Enable or disable a multicast addr */
usbecm_m_unicst, /* Set the unicast MAC address */
usbecm_m_tx, /* Transmit a packet */
NULL,
usbecm_m_ioctl, /* Process an unknown ioctl */
NULL, /* mc_getcapab */
NULL, /* mc_open */
NULL, /* mc_close */
usbecm_m_setprop, /* mc_setprop */
usbecm_m_getprop, /* mc_getprop */
};
/*
* Module Loading Data & Entry Points
* Can't use DDI_DEFINE_STREAM_OPS, since it does
* not provide devo_power entry.
*/
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_stream */
D_MP, /* cb_flag */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev, /* cb_awrite */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
usbecm_attach, /* devo_attach */
usbecm_detach, /* devo_detach */
nodev, /* devo_reset */
&(cb_usbecm), /* devo_cb_ops */
usbecm_power, /* devo_power */
ddi_quiesce_not_needed /* devo_quiesce */
};
&mod_driverops, /* drv_modops */
usbecm_ident, /* drv_linkinfo */
&usbecm_devops /* drv_dev_ops */
};
MODREV_1, /* ml_rev */
};
/*
* Device operations
*/
/*
* Binding the driver to a device.
*
* Concurrency: Until usbecm_attach() returns with success,
* the only other entry point that can be executed is getinfo().
* Thus no locking here yet.
*/
static int
{
int instance;
int err;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
(void) usbecm_resume(ecmp);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
return (DDI_FAILURE);
}
"usbecm_attach: failed to init usb");
goto fail;
}
if (ECM_DS_OP_VALID(ecm_ds_init)) {
"usbecm_attach: failed to init DS");
goto fail;
}
}
"usbecm_attach: failed to init mac");
goto fail;
}
/*
* Create minor node of type usb_net. Not necessary to create
* DDI_NT_NET since it's created in mac_register(). Otherwise,
* system will panic.
*/
if (err != DDI_SUCCESS) {
"failed to create minor node");
goto fail;
}
/* always busy. May change to a more precise PM in future */
"usbecm_attach: succeed!");
return (DDI_SUCCESS);
fail:
"usbecm_attach: Attach fail");
return (DDI_FAILURE);
}
/*
* Detach the driver from a device.
*
* Concurrency: Will be called only after a successful attach
* (and not concurrently).
*/
static int
{
int instance;
"usbecm_detach: entry ");
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (usbecm_suspend(ecmp));
default:
return (DDI_FAILURE);
}
if (ECM_DS_OP_VALID(ecm_ds_fini)) {
"usbecm_detach: deinitialize DS fail!");
return (DDI_FAILURE);
}
}
if (usbecm_mac_fini(ecmp) != 0) {
return (DDI_FAILURE);
}
"usbecm_detach: exit");
return (DDI_SUCCESS);
}
/*
* Mac Call Back functions
*/
/*
* Read device statistic information.
*/
static int
{
int rval;
"usbecm_m_stat: entry, stat=%d", stat);
/*
* Some of the stats are MII specific. We try to
* resolve all the statistics we understand. If
* the usb device can't provide it, return ENOTSUP.
*/
switch (stat) {
case MAC_STAT_IFSPEED:
/* return link speed */
} else {
}
return (0);
case ETHER_STAT_LINK_DUPLEX:
*val = LINK_DUPLEX_FULL;
return (0);
case ETHER_STAT_SQE_ERRORS:
*val = 0;
return (0);
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_NOXMTBUF:
fs = ECM_XMIT_ERROR;
break;
case MAC_STAT_IERRORS:
fs = ECM_RCV_ERROR;
break;
case MAC_STAT_OERRORS:
fs = ECM_XMIT_ERROR;
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_COLLISIONS:
break;
case MAC_STAT_OVERFLOWS:
break;
case MAC_STAT_UNDERFLOWS:
break;
case ETHER_STAT_FCS_ERRORS:
break;
case ETHER_STAT_ALIGN_ERRORS:
break;
case ETHER_STAT_DEFER_XMTS:
break;
break;
break;
break;
default:
return (ENOTSUP);
}
/*
* we need to access device to get required stats,
* so check device state first
*/
"usbecm_m_stat: device not ONLINE");
return (EIO);
}
if (rval != USB_SUCCESS) {
switch (stat) {
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_BRDCSTXMT:
break;
case ETHER_STAT_MACXMT_ERRORS:
break;
default:
*val = 0;
break;
}
} else {
}
"usbecm_m_stat: end");
return (0);
}
/*
* Start the device:
* - Set proper altsettings of the data interface
* - Open status and data endpoints
* - Start status polling
* - Get bulk-in ep ready to receive data from ethernet
*
* Concurrency: Presumably fully concurrent, must lock.
*/
static int
{
int rval;
"usbecm_m_start: entry");
"usbecm_m_start: device not online");
goto fail;
}
"usbecm_m_start: open pipes fail");
goto fail;
}
"usbecm_m_start: fail to start_rx");
goto fail;
}
if (rval != USB_SUCCESS) {
"usbecm_m_start: set packet filters fail,"
" rval=%d, continue", rval);
}
if (ECM_DS_OP_VALID(ecm_ds_start)) {
"usbecm_m_start: Can't start hardware");
goto fail;
}
}
"usbecm_m_start: end");
/*
* To mark the link as RUNNING.
*
* ECM spec doesn't provide a way for host to get the status
* of the physical link initiatively. Only the device can
* report the link state through interrupt endpoints.
*/
return (DDI_SUCCESS);
fail:
return (rval);
}
/*
* Stop the device.
*/
static void
{
"usbecm_m_stop: entry");
if (ECM_DS_OP_VALID(ecm_ds_stop)) {
"usbecm_m_stop: fail to stop hardware");
}
}
"usbecm_m_stop: end");
}
/*
* Change the MAC address of the device.
*/
/*ARGSUSED*/
static int
{
int rval;
"usbecm_m_unicst: entry");
/*
* The device doesn't support to set a different MAC addr.
* Hence, it's not necessary to stop the device first if
* the mac addresses are identical. And we just set unicast
* filter only.
*/
"usbecm_m_unicst: not supported to set a"
" different MAC addr");
return (DDI_FAILURE);
}
"usbecm_m_unicst: rval = %d", rval);
/* some devices may not support this request, we just return success */
return (DDI_SUCCESS);
}
/*
*/
/*ARGSUSED*/
static int
{
int rval = 0;
"usbecm_m_multicst: entry");
/*
* To simplify the implementation, we support switching
*/
} else {
}
if (ecmp->ecm_compatibility &&
/* Device supports SetEthernetMulticastFilters request */
"usbecm_m_multicst: rval = %d", rval);
}
/* some devices may not support this request, we just return success */
return (DDI_SUCCESS);
}
/*
*/
static int
{
int rval;
"usbecm_m_promisc: entry");
"usbecm_m_promisc: device not ONLINE");
return (DDI_FAILURE);
}
} else {
}
"usbecm_m_promisc: rval=%d", rval);
/*
* devices may not support this request, we just
* return success to let upper layer to do further
* operation.
*/
return (DDI_SUCCESS);
}
/*
* IOCTL request: Does not do anything. Will be enhanced
* in future.
*/
static void
{
int cmd;
"usbecm_m_ioctl: entry");
"usbecm_m_ioctl: device not ONLINE");
return;
}
switch (cmd) {
default:
"unknown cmd 0x%x", cmd);
return;
}
}
/*
* Does not do anything. Will be enhanced to
*/
/*ARGSUSED*/
static int
{
"usbecm_m_setprop: entry");
return (err);
}
/*ARGSUSED*/
{
"usbecm_m_getprop: entry");
return (EIO);
}
return (err);
}
/*
* Transmit a data frame.
*/
static mblk_t *
{
int count = 0;
"usbecm_m_tx: entry");
"usbecm_m_tx: device not ONLINE");
return (mp);
}
/*
* To make use of the device maximum capability,
* concatenate msg blocks in a msg to ETHERMAX length.
*/
"usbecm_m_tx: send data fail");
/* failure statistics */
break;
}
/*
* To make it simple, we count all packets, no matter
* the device supports ethernet statistics or not.
*/
"usbecm_m_tx: %d msgs processed", ++count);
}
return (mp);
}
/*
* usbecm_bulkin_cb:
* Bulk In regular and exeception callback;
* USBA framework will call this callback
* after deal with bulkin request.
*/
/*ARGSUSED*/
static void
{
int data_len;
data_len);
/*
* may receive a zero length packet according
* to USB short packet semantics
*/
if (data_len) {
} else {
> ETHERMAX) {
/*
* Exceed the ethernet maximum length, we think
* something is wrong with this frame and hence
* free older data. Accept new data instead.
*/
} else {
}
}
} else {
/*
* Do not put zero length packet to receive queue.
* Otherwise, msgpullup will dupmsg() a zero length
* mblk, which will cause memleaks.
*/
}
/*
* ECM V1.2, section 3.3.1, a short(including zero length)
* packet signifies end of frame. We can submit this frame
* to upper layer now.
*/
if ((data_len < max_pkt_size) &&
ETHERADDRL) != 0) {
} else {
}
}
if (mp) {
}
}
/* prevent USBA from freeing data along with the request */
}
/* receive more */
"usbecm_bulkin_cb: restart rx fail "
}
}
}
/*
* usbsecm_rx_start:
* start data receipt
*/
static int
{
int data_len;
int, ecmp->ecm_bulkin_sz);
"usbsecm_rx_start: allocate bulk request failed");
return (USB_FAILURE);
}
/* initialize bulk in request. */
br->bulk_timeout = 0;
if (rval != USB_SUCCESS) {
"usbsecm_rx_start: bulk transfer failed %d", rval);
}
return (rval);
}
/*
* usbecm_bulkout_cb:
* Bulk Out regular and exeception callback;
* USBA framework will call this callback function
* after deal with bulkout request.
*/
/*ARGSUSED*/
static void
{
int data_len;
"usbecm_bulkout_cb: data_len = %d, cr=%d", data_len,
}
ecmp->ecm_tx_cnt--;
}
}
/*
* notify MAC layer to retransfer the failed packet
* Or notity MAC that we have more buffer now.
*/
if (need_update) {
}
}
static int
{
int max_pkt_size;
int new_data_len = 0;
"usbecm_send_data: length = %d, total len=%d",
"usbecm_send_data: (%d) exceeds TX max queue length",
ecmp->ecm_tx_cnt);
return (USB_FAILURE);
}
"usbecm_send_data: packet too long, %d", data_len);
return (USB_FAILURE);
}
"usbecm_send_data: short packet, padding to ETHERMIN");
"usbecm_send_data: fail to allocb");
return (USB_FAILURE);
}
}
}
"usbecm_send_data: alloc req failed.");
return (USB_FAILURE);
}
/* initialize the bulk out request */
if (new_data) {
} else {
}
ETHERADDRL) != 0) {
} else {
}
}
}
if (rval != USB_SUCCESS) {
"usbecm_send_data: Send Data failed.");
/*
* br->bulk_data should be freed because we allocated
* it in this function.
*/
} else {
ecmp->ecm_tx_cnt++;
/*
* ECM V1.2, section 3.3.1, a short(including zero length)
* packet signifies end of frame. We should send a zero length
* packet to device if the total data lenght is multiple of
* bulkout endpoint's max packet size.
*/
if ((data_len % max_pkt_size) == 0) {
!= USB_SUCCESS) {
"usbecm_send_data: fail to send padding");
}
}
}
if (new_data) {
}
"usbecm_send_data: len(%d) data sent, rval=%d",
return (rval);
}
static int
{
"usbecm_send_zero_data: entry");
"usbecm_send_data: alloc req failed.");
return (USB_FAILURE);
}
/* initialize the bulk out request */
if (rval != USB_SUCCESS) {
"usbecm_send_zero_data: Send data failed, rval=%d",
rval);
/*
* br->bulk_data should be freed because we allocated
* it in this function.
*/
}
"usbecm_send_zero_data: end");
return (rval);
}
/*
* Loadable module configuration entry points
*/
/*
* _init module entry point.
*
* Called when the module is being loaded into memory.
*/
int
_init(void)
{
int err;
if (err != DDI_SUCCESS)
return (err);
if (err != DDI_SUCCESS) {
}
return (err);
}
/*
* _info module entry point.
*
* Called to obtain information about the module.
*/
int
{
}
/*
* _fini module entry point.
*
* Called when the module is being unloaded.
*/
int
_fini(void)
{
int err;
if (err == DDI_SUCCESS) {
}
return (err);
}
/*
* usbecm_pipe_start_polling:
* start polling on the interrupt pipe
*/
static void
{
int rval;
"usbecm_pipe_start_polling: ");
return;
}
/*
* If it is in interrupt context, usb_alloc_intr_req will return NULL if
* called with SLEEP flag.
*/
if (!intr) {
"usbecm_pipe_start_polling: alloc req failed.");
return;
}
/* initialize the interrupt request. */
if (rval == USB_SUCCESS) {
} else {
"usbecm_pipe_start_polling: failed (%d)", rval);
}
"usbecm_pipe_start_polling: end, rval=%d", rval);
}
/*
* usbsecm_intr_cb:
* interrupt pipe normal callback
*/
/*ARGSUSED*/
static void
{
int data_len;
/* check data length */
if (data_len < 8) {
"usbsecm_intr_cb: %d packet too short", data_len);
return;
}
/* parse interrupt data -- notifications */
}
/*
* usbsecm_intr_ex_cb:
* interrupt pipe exception callback
*/
/*ARGSUSED*/
static void
{
/*
* If completion reason isn't USB_CR_PIPE_CLOSING and
* USB_CR_STOPPED_POLLING, restart polling.
*/
"usbsecm_intr_ex_cb: state = %d",
return;
}
}
}
/*
* usbsecm_parse_intr_data:
* Parse data received from interrupt callback
*/
static void
{
int linkstate;
/*
* If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
* mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
* mLength is 2. So we directly get the value from the byte.
*/
if (ecmp->ecm_compatibility) {
"usbsecm_parse_intr_data: unknown request "
"type - 0x%x", bmRequestType);
return;
}
} else {
/* non-compatible device specific parsing */
if (ECM_DS_OP_VALID(ecm_ds_intr_cb)) {
!= USB_SUCCESS) {
"usbsecm_parse_intr_data: unknown request"
"type - 0x%x", bmRequestType);
}
}
return;
}
/*
* Check the return value of compatible devices
*/
switch (bNotification) {
"usbsecm_parse_intr_data: %s network!",
/* no changes to previous state */
break;
}
break;
"usbsecm_parse_intr_data: A response is a available.");
break;
"usbsecm_parse_intr_data: speed change");
/* check the parameter's length. */
if (wLength != 8) {
"usbsecm_parse_intr_data: error data length.");
} else {
}
break;
default:
"usbsecm_parse_intr_data: unknown notification - 0x%x!",
break;
}
}
/*
* usbecm_restore_device_state:
* restore device state after CPR resume or reconnect
*/
static int
{
int state;
"usbecm_restore_device_state: ");
/* Check device status */
return (state);
}
/* Check if we are talking to the same device */
return (state);
}
if (state == USB_DEV_DISCONNECTED) {
"usbecm_restore_device_state: Device has been reconnected "
"but data may have been lost");
}
/* if MAC was started, restarted it */
"usbecm_restore_device_state: MAC was started");
/* Do the same operation as usbecm_m_start() does */
return (state);
}
return (state);
}
}
/*
* init device state
*/
return (state);
}
/*
* usbecm_reconnect_event_cb:
* called upon when the device is hotplugged back
*/
/*ARGSUSED*/
static int
{
"usbecm_reconnect_event_cb: entry");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* usbecm_disconnect_event_cb:
* callback for disconnect events
*/
/*ARGSUSED*/
static int
{
"usbecm_disconnect_event_cb: entry");
"usbecm_disconnect_event_cb: End");
return (USB_SUCCESS);
}
/*
* power management
* ----------------
*
* usbecm_create_pm_components:
* create PM components
*/
static int
{
"usbecm_create_pm_components: entry");
"usbecm_create_pm_components: failed");
/* don't fail the attach process */
return (USB_SUCCESS);
}
return (USB_SUCCESS);
}
/*
* usbecm_cleanup:
* Release resources of current device during detach.
*/
static void
{
"usbecm_cleanup: ");
return;
}
/* unregister callback function */
"usbecm_cleanup: unregister events");
}
/* destroy power management components */
"usbecm_cleanup: destroy pm");
}
/* free description of device tree. */
}
}
/* detach client device */
}
(void) usbecm_mac_fini(ecmp);
}
}
}
/*
* usbecm_destroy_pm_components:
* destroy PM components
*/
static void
{
int rval;
"usbecm_destroy_pm_components: ");
if (pm->pm_wakeup_enabled) {
if (rval != DDI_SUCCESS) {
"usbecm_destroy_pm_components: "
"raising power failed (%d)", rval);
}
if (rval != USB_SUCCESS) {
"usbecm_destroy_pm_components: "
"disable remote wakeup failed (%d)", rval);
}
}
}
}
/*
* usbecm_pm_set_busy:
* mark device busy and raise power
*/
static void
{
int rval;
"usbecm_pm_set_busy: pm = 0x%p", (void *)pm);
return;
}
/* if already marked busy, just increment the counter */
if (pm->pm_busy_cnt++ > 0) {
return;
}
(void) pm_busy_component(dip, 0);
return;
}
/* need to raise power */
if (rval != DDI_SUCCESS) {
"usbecm_pm_set_busy: raising power failed");
}
}
/*
* usbecm_pm_set_idle:
* mark device idle
*/
static void
{
"usbecm_pm_set_idle: ");
return;
}
if (--pm->pm_busy_cnt > 0) {
return;
}
if (pm) {
(void) pm_idle_component(dip, 0);
}
}
/*
* usbecm_pwrlvl0:
* Functions to handle power transition for OS levels 0 -> 3
* The same level as OS state, different from USB state
*/
static int
{
int rval;
"usbecm_pwrlvl0: ");
switch (ecmp->ecm_dev_state) {
case USB_DEV_ONLINE:
/* issue USB D3 command to the device */
}
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
/* allow a disconnect/cpr'ed device to go to lower power */
return (USB_SUCCESS);
case USB_DEV_PWRED_DOWN:
default:
"usbecm_pwrlvl0: illegal device state");
return (USB_FAILURE);
}
}
/*
* usbecm_pwrlvl1:
* Functions to handle power transition for OS levels 1 -> 2
*/
static int
{
/* issue USB D2 command to the device */
return (USB_FAILURE);
}
/*
* usbecm_pwrlvl2:
* Functions to handle power transition for OS levels 2 -> 1
*/
static int
{
/* issue USB D1 command to the device */
return (USB_FAILURE);
}
/*
* usbecm_pwrlvl3:
* Functions to handle power transition for OS levels 3 -> 0
* The same level as OS state, different from USB state
*/
static int
{
int rval;
"usbecm_pwrlvl3: ");
switch (ecmp->ecm_dev_state) {
case USB_DEV_PWRED_DOWN:
/* Issue USB D0 command to the device here */
}
/* FALLTHRU */
case USB_DEV_ONLINE:
/* we are already in full power */
/* FALLTHRU */
case USB_DEV_DISCONNECTED:
case USB_DEV_SUSPENDED:
return (USB_SUCCESS);
default:
"usbecm_pwrlvl3: illegal device state");
return (USB_FAILURE);
}
}
/*ARGSUSED*/
static int
{
"usbecm_power: entry");
/* check if pm is NULL */
"usbecm_power: pm is NULL.");
return (USB_FAILURE);
}
/*
* check if we are transitioning to a legal power level
*/
"usbecm_power: "
"illegal power level %d, pwr_states=%x",
return (USB_FAILURE);
}
/*
* if we are about to raise power and asked to lower power, fail
*/
"usbecm_power: wrong condition.");
return (USB_FAILURE);
}
/*
* Set the power status of device by request level.
*/
switch (level) {
case USB_DEV_OS_PWR_OFF:
break;
case USB_DEV_OS_PWR_1:
break;
case USB_DEV_OS_PWR_2:
break;
case USB_DEV_OS_FULL_PWR:
break;
}
return (rval);
}
/*
* Register with the MAC layer.
*/
static int
{
int err;
/*
* Initialize mac structure
*/
"failed to allocate MAC structure");
return (USB_FAILURE);
}
/*
* Initialize pointer to device specific functions
*/
/*
* Register the macp to mac
*/
if (err != DDI_SUCCESS) {
"failed to register MAC structure");
return (USB_FAILURE);
}
ecmp->ecm_tx_cnt = 0;
return (USB_SUCCESS);
}
static int
{
return (DDI_SUCCESS);
}
"failed to disable MAC");
return (rval);
}
return (rval);
}
static int
{
int current_state;
int ret;
"usbecm_resume: ");
/* restore the status of device */
if (current_state != USB_DEV_ONLINE) {
} else {
}
return (ret);
}
static int
{
return (0);
}
/*
* Translate MAC address from string to 6 bytes array int value
* Can't use ether_aton() since it requires format of x:x:x:x:x:x
*/
void
{
int i;
char c;
/* can only count 6 bytes! */
for (i = 0; i < 6; i++) {
/* upper 4 bits */
} else {
}
mac[i] = c * 16;
/* lower 4 bits */
} else {
}
mac[i] += c;
}
}
/*
* usbecm_get_descriptors:
* parse functional descriptors of ecm compatible device
*/
static int
{
int i;
"usbecm_get_descriptors: ");
/*
* Special treatment of Sun's SP Ethernet device.
*/
"usbecm_get_descriptors: fail to set cfg ");
} else {
USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
"usbecm_get_descriptors: fail to get"
" dev_data");
return (USB_FAILURE);
}
}
}
/* set default control and data interface */
/* get current interfaces */
"usbecm_get_descriptors: elements in if_alt is %d",
return (USB_FAILURE);
}
/*
* Based on CDC specification, ECM devices usually include the
* following function descriptors: Header, Union and ECM
* Contry Selection function descriptors. This loop search tree data
* structure for each ecm class descriptor.
*/
for (i = 0; i < altif->altif_n_cvs; i++) {
continue;
}
/*
* parse header functional descriptor
* Just to check integrity.
*/
return (USB_FAILURE);
}
break;
/* parse ECM functional descriptor */
return (USB_FAILURE);
}
/* get the MAC address */
USB_MAXSTRINGLEN) != USB_SUCCESS) {
return (USB_FAILURE);
}
"usbecm_get_descriptors: macaddr=%s ",
buf);
/* expects 12 characters */
return (USB_FAILURE);
}
}
break;
case USB_CDC_DESCR_TYPE_UNION:
/* parse Union functional descriptor. */
}
break;
default:
break;
}
}
/* For usb ecm devices, it must satisfy the following options. */
"usbecm_get_descriptors: # of interfaces %d < 2",
return (USB_FAILURE);
}
if (ecmp->ecm_data_if_no == 0 &&
"usbecm_get_descriptors: Device has no call management "
"descriptor and use Union Descriptor.");
}
"usbecm_get_descriptors: control interface or "
"data interface don't match.");
return (USB_FAILURE);
}
"usbecm_get_descriptors: control interface %d or "
"data interface %d out of range.",
return (USB_FAILURE);
}
/* ECM data interface has a minimal of two altsettings */
"usbecm_get_descriptors: elements in if_alt is %d,"
return (USB_FAILURE);
}
/* control interface must have interrupt endpoint */
USB_EP_DIR_IN)) == NULL) {
"usbecm_get_descriptors: "
"ctrl interface %d has no interrupt endpoint",
return (USB_FAILURE);
}
/* data interface alt 1 must have bulk in and out(ECM v1.2,p5) */
USB_EP_DIR_IN)) == NULL) {
"usbecm_get_descriptors: "
"data interface %d has no bulk in endpoint",
return (USB_FAILURE);
}
USB_EP_DIR_OUT)) == NULL) {
"usbecm_get_descriptors: "
"data interface %d has no bulk out endpoint",
return (USB_FAILURE);
}
/* set default value for ethernet packet filter */
return (USB_SUCCESS);
}
/* Generate IEEE802 style MAC address */
static void
{
}
/*
*/
{
/*
* for the non-compatible devices, to make it simple, we
* suppose the devices have this kind of configuration:
* INTR In EP(if exists) + BULK In + Bulk Out in the
* same altsetting of the same interface
*/
USB_EP_DIR_IN)) == NULL) ||
USB_EP_DIR_OUT)) == NULL) {
continue;
}
/*
* search interrupt pipe.
*/
}
return (USB_SUCCESS);
}
return (USB_FAILURE);
}
static int
{
/*
* endpoints and fill related structure. We suppose this driver
* is bound to a interface.
*/
/* search each interface which have bulk in and out */
for (i = 0; i < if_num; i++) {
if (usbecm_find_bulk_in_out_eps(ecmp, i,
cur_if) == USB_SUCCESS) {
break;
}
cur_if++;
}
"usbecm_init_non_compatible_device: ctrl_if=%d,"
return (USB_SUCCESS);
}
static boolean_t
{
int i, j, cfg_index;
"usbecm_is_compatible: entry, cfg_num=%d", cfg_num);
"usbecm_is_compatible: cfg_index=%d, value=%d",
for (i = 0; i < if_num; i++) {
for (j = 0; j < alt_num; j++) {
"usbecm_is_compatible: cfg_index=%d",
return (B_TRUE);
}
}
intf++;
}
}
return (B_FALSE);
}
static int
{
USB_SUCCESS) {
"usbecm_usb_init: fail to attach");
return (USB_FAILURE);
}
/* Get the configuration information of device */
USB_PARSE_LVL_ALL, 0) != USB_SUCCESS) {
"usbecm_usb_init: fail to get_dev_data");
return (USB_FAILURE);
}
"usbif,class2.6") == 0) ||
"usb,class2.6.0") == 0))) {
"usbecm_usb_init: A CDC ECM device is attached");
/*
* Current Sun SP ECM device has two configurations. Hence
* USBA doesn't create interface level compatible names
* for it, see usba_ready_device_node(). We have to check
* manually to see if compatible interfaces exist, when
* the driver owns the entire device.
*/
"usbecm_usb_init: A CDC ECM device is attached");
} else {
"usbecm_usb_init: A nonstandard device is attached to "
"usbecm(7D) driver. This device doesn't conform to "
"usb cdc spec.");
/* generate a random MAC addr */
}
"usbecm_usb_init: A compatible device is attached, but "
"fail to get standard descriptors");
return (USB_FAILURE);
}
(void) usbecm_init_non_compatible_device(ecmp);
}
/* Create power management components */
"usbecm_usb_init: create pm components failed.");
return (USB_FAILURE);
}
/* Register to get callbacks for USB events */
!= USB_SUCCESS) {
"usbsecm_attach: register event callback failed.");
return (USB_FAILURE);
}
/* Get max data size of bulk transfer */
"usbsecm_ds_attach: get max size of transfer failed.");
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
/*
* Open operation pipes. Each ECM device should have Bulk In, Bulk Out
* and Interrupt In endpoints
*/
static int
{
int altif;
"usbsecm_open_pipes: ecmp = 0x%p", (void *)ecmp);
/* compatible device has minimum of 2 altsetting, select alt 1 */
altif = 1;
} else {
}
/* Bulk in and out must exist simultaneously. */
"usbsecm_open_pipes: look up bulk pipe failed in "
"interface %d ",
return (USB_FAILURE);
}
/*
* If device conform to ecm spec, it must have an interrupt pipe
* for this device.
*/
"usbecm_open_pipes: look up interrupt pipe failed in "
return (USB_FAILURE);
}
"usbsecm_open_pipes: open intr %02x, bulkin %02x bulkout %02x",
"usbsecm_open_pipes: set data if(%d) alt(%d) ",
"usbecm_open_pipes: set alternate failed (%d)",
rval);
return (rval);
}
/* Open bulk in endpoint */
"usbecm_open_pipes: open bulkin pipe failed!");
return (USB_FAILURE);
}
/* Open bulk out endpoint */
"usbecm_open_pipes: open bulkout pipe failed!");
return (USB_FAILURE);
}
/* Open interrupt endpoint if found. */
"usbecm_open_pipes: "
"open intr pipe failed");
return (USB_FAILURE);
}
}
/* initialize the pipe related data */
}
}
"usbsecm_open_pipes: end");
return (rval);
}
/*
* usbsecm_close_pipes:
* Close pipes
* Each device could include three pipes: bulk in, bulk out and interrupt.
*/
static void
{
"usbsecm_close_pipes: ecm_bulkin_state = %d",
/*
* Check the status of the pipes. If pipe is closing or closed,
* return directly.
*/
"usbsecm_close_pipes: pipe is closing or has closed");
return;
}
/* reset the data interface's altsetting to 0 */
"usbecm_close_pipes: reset alternate failed ");
}
/* Close pipes */
USB_FLAGS_SLEEP, NULL, 0);
USB_FLAGS_SLEEP, NULL, 0);
USB_FLAGS_SLEEP, NULL, 0);
USB_FLAGS_SLEEP, NULL, 0);
}
/* Reset the status of pipes to closed */
}
"usbsecm_close_pipes: pipes have been closed.");
}
static int
{
int rval;
"usbecm_ctrl_write: ");
/* initialize the control request. */
"usbecm_ctrl_write: rval = %d", rval);
return (rval);
}
static int
{
"usbecm_ctrl_read: ");
/* initialize the control request. */
}
/* Get specific statistic data from device */
static int
{
/* first check to see if this stat is collected by device */
return (USB_FAILURE);
}
return (USB_SUCCESS);
}
return (USB_FAILURE);
}