/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2007 by Lukas Turek <turek@ksvi.mff.cuni.cz>
* Copyright (c) 2007 by Jiri Svoboda <jirik.svoboda@seznam.cz>
* Copyright (c) 2007 by Martin Krulis <martin.krulis@matfyz.cz>
* Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
* Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/*
* ZD1211 wLAN driver
* Driver major routines
*/
#include <sys/byteorder.h>
#include <sys/mac_provider.h>
#include <sys/mac_wifi.h>
#include "zyd.h"
#include "zyd_reg.h"
static int zyd_m_start(void *arg);
static void zyd_m_stop(void *arg);
/* Driver identification */
/* Global state pointer for managing per-device soft states */
void *zyd_ssp;
/*
* Mac Call Back entries
*/
zyd_m_stat, /* Get the value of a statistic */
zyd_m_start, /* Start the device */
zyd_m_stop, /* Stop the device */
zyd_m_promisc, /* Enable or disable promiscuous mode */
zyd_m_multicst, /* Enable or disable a multicast addr */
zyd_m_unicst, /* Set the unicast MAC address */
zyd_m_tx, /* Transmit a packet */
NULL,
zyd_m_ioctl, /* Process an unknown ioctl */
NULL, /* mc_getcapab */
NULL,
NULL,
};
/*
* Module Loading Data & Entry Points
*/
nulldev, /* identify */
nulldev, /* probe */
zyd_attach, /* attach */
zyd_detach, /* detach */
nodev, /* reset */
NULL, /* getinfo */
D_MP, /* flag */
NULL, /* stream_tab */
ddi_quiesce_not_needed /* quiesce */
);
&mod_driverops, /* drv_modops */
zyd_ident, /* drv_linkinfo */
&zyd_devops /* drv_dev_ops */
};
MODREV_1, /* ml_rev */
};
/*
* Wireless-specific structures
*/
4, {2, 4, 11, 22} /* units are 0.5Mbit! */
};
12, {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108}
};
#ifdef DEBUG
void
{
if (dbg_mask & zyd_dbg_flags) {
}
}
#endif
void
{
}
/*
* Internal functions
*/
static uint8_t
{
switch (rate) {
/* CCK rates (returned values are device-dependent) */
case 2:
return (0x0);
case 4:
return (0x1);
case 11:
return (0x2);
case 22:
return (0x3);
/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
case 12:
return (0xb);
case 18:
return (0xf);
case 24:
return (0xa);
case 36:
return (0xe);
case 48:
return (0x9);
case 72:
return (0xd);
case 96:
return (0x8);
case 108:
return (0xc);
/* unsupported rates (should not get there) */
default:
return (0xff);
}
}
/*
* Timeout function for scanning.
*
* Called at the end of each scanning round.
*/
static void
{
} else {
}
}
/*
* Extract a 802.11 frame from the received packet and forward it to net80211.
*/
void
{
mblk_t *m;
if (len < ZYD_MIN_FRAGSZ) {
/* Packet is too short, silently drop it */
return;
}
stat = (const struct zyd_rx_stat *)
/* Frame is corrupted, silently drop it */
return;
}
/* compute actual frame length */
sizeof (struct zyd_rx_stat) - IEEE80211_CRC_LEN;
if (m == NULL) {
return;
}
/* Copy frame to new buffer */
/* Send frame to net80211 stack */
}
/*
* xxx_send callback for net80211.
*
* Transmit a 802.11 frame.
*
* Constructs a packet from zyd_tx_header and 802.11 frame data
* and sends it to the chip.
*/
/*ARGSUSED*/
static int
{
struct ieee80211_key *k;
int res;
/* device not ready, drop all frames */
if (type == IEEE80211_FC0_TYPE_DATA)
return (DDI_SUCCESS);
else
return (DDI_FAILURE);
}
/* device queue overrun */
/* drop management frames */
if (type != IEEE80211_FC0_TYPE_DATA) {
} else {
}
return (DDI_FAILURE);
}
BPRI_MED);
if (m == NULL) {
return (DDI_FAILURE);
}
m->b_rptr += sizeof (struct zyd_tx_header);
}
freemsg(m);
return (DDI_SUCCESS);
}
if (type == IEEE80211_FC0_TYPE_DATA)
k = ieee80211_crypto_encap(ic, m);
if (k == NULL) {
freemsg(m);
return (DDI_SUCCESS);
}
/* packet header may have moved, reset our local pointer */
}
/*
* pickup a rate. May need work to make adaptive - at present,
* picks best rate for mode.
*/
if (type == IEEE80211_FC0_TYPE_MGT) {
/* mgmt frames are sent at 1M */
} else {
}
if (rate == 0) /* should not happen */
rate = 2;
/* Get total length of frame */
m->b_rptr -= sizeof (struct zyd_tx_header);
/*
* Compute "packet size". What the 10 stands for,
* nobody knows.
*/
else
/* multicast frames are not sent at OFDM rates in 802.11b/g */
} else if (ZYD_RATE_IS_OFDM(rate) &&
buf_hdr->type_flags |=
}
} else
if ((type == IEEE80211_FC0_TYPE_CTL) &&
if (ZYD_RATE_IS_OFDM(rate)) {
/*
* Compute frame duration and length-extension service flag.
*/
service = 0x00;
if (rate == 22) {
}
if (res != ZYD_SUCCESS) {
} else {
}
return (DDI_SUCCESS);
}
/*
* Register with the MAC layer.
*/
static zyd_res
{
int err;
/*
* Initialize mac structure
*/
ZYD_WARN("failed to allocate MAC structure\n");
return (ZYD_FAILURE);
}
/*
* Initialize pointer to device specific functions
*/
/*
* Register the macp to mac
*/
if (err != DDI_SUCCESS) {
ZYD_WARN("failed to register MAC structure\n");
return (ZYD_FAILURE);
}
return (ZYD_SUCCESS);
}
/*
* Register with net80211.
*/
static void
{
int i;
/*
* Initialize the WiFi part, which will be used by generic layer
*/
/* set device capabilities */
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
/* Copy MAC address */
/*
* set supported .11b and .11g rates
*/
/*
* set supported .11b and .11g channels(1 through 14)
*/
for (i = 1; i <= 14; i++) {
}
/*
* Init generic layer (it cannot fail)
*/
/* register WPA door */
/* Must be after attach! */
ic->ic_def_txkey = 0;
}
/*
* Device operations
*/
/*
* Binding the driver to a device.
*
* Concurrency: Until zyd_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) zyd_resume(sc);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (err != DDI_SUCCESS) {
ZYD_WARN("failed to allocate soft state\n");
return (DDI_FAILURE);
}
sc->timeout_id = 0;
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Create minor node of type DDI_NT_NET_WIFI
*/
if (err != DDI_SUCCESS)
ZYD_WARN("failed to create minor node\n");
/* initialize locking */
return (DDI_SUCCESS);
}
/*
* Detach the driver from a device.
*
* Concurrency: Will be called only after a successful attach
* (and not concurrently).
*/
static int
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (zyd_suspend(sc));
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/*
* Unregister from the MAC layer subsystem
*/
/*
* Detach ieee80211
*/
/* At this point it should be safe to release & destroy the locks */
return (DDI_SUCCESS);
}
/*
* Mac Call Back functions
*/
/*
* Read device statistic information.
*/
static int
{
switch (stat) {
case MAC_STAT_IFSPEED:
return (ENOTSUP);
break;
case MAC_STAT_NOXMTBUF:
break;
case MAC_STAT_NORCVBUF:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OERRORS:
case WIFI_STAT_TX_FAILED:
break;
case WIFI_STAT_TX_RETRANS:
case WIFI_STAT_FCS_ERRORS:
case WIFI_STAT_WEP_ERRORS:
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_RTS_SUCCESS:
case WIFI_STAT_RTS_FAILURE:
case WIFI_STAT_ACK_FAILURE:
case WIFI_STAT_RX_FRAGS:
case WIFI_STAT_MCAST_RX:
case WIFI_STAT_RX_DUPS:
default:
return (ENOTSUP);
}
return (0);
}
/*
* Start the device.
*
* Concurrency: Presumably fully concurrent, must lock.
*/
static int
{
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* Stop the device.
*/
static void
{
}
/*
* Change the MAC address of the device.
*/
/*ARGSUSED*/
static int
{
return (DDI_FAILURE);
}
/*
*/
/*ARGSUSED*/
static int
{
return (DDI_SUCCESS);
}
/*
*/
/*ARGSUSED*/
static int
{
return (DDI_SUCCESS);
}
/*
* IOCTL request.
*/
static void
{
return;
}
zyd_m_stop(sc);
return;
}
}
}
/*
*/
static int
{
int err;
return (ENXIO);
}
wldp_buf);
zyd_m_stop(sc);
return (DDI_FAILURE);
}
err = 0;
}
return (err);
}
static int
{
int err;
return (DDI_FAILURE);
}
return (err);
}
static void
{
}
/*
* Transmit a data frame.
*/
static mblk_t *
{
/* not associated, drop data frames */
return (DDI_SUCCESS);
}
break;
}
}
return (mp);
}
/*
* xxx_newstate callback for net80211.
*
* Called by net80211 whenever the ieee80211 state changes.
*/
static int
{
if (sc->timeout_id != 0) {
sc->timeout_id = 0;
}
}
switch (nstate) {
case IEEE80211_S_SCAN:
/*FALLTHRU*/
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
case IEEE80211_S_RUN:
ZYD_WARN("invalid channel number\n");
return (0);
}
default:
break;
}
}
/*
* USB-safe synchronization.
* Debugging routines.
*
* Kmutexes should never be held when making calls to USBA
* or when sleeping. Thus, we implement our own "mutex" on top
* of kmutexes and kcondvars.
*
* Usage: Any (possibly concurrent) access to the soft state or device must
* be serialized with a pair of zyd_serial_enter()/zyd_serial_exit().
*/
/*
* Initialize the serialization object.
*/
void
{
}
/*
* Wait for the serialization object.
*
* If wait_sig is ZYD_SER_SIG, the function may return
* a signal is received. In this case, the serialization object
* is not acquired (but the mutex is) and the return value is ZYD_FAILURE.
*
* In any other case the function returns ZYD_SUCCESS and the
* serialization object is acquired.
*/
{
res = ZYD_SUCCESS;
if (wait_sig == ZYD_SER_SIG) {
} else {
}
}
return (res);
}
/*
* Release the serialization object.
*/
void
{
}
/*
* Destroy the serialization object.
*/
void
{
}
/*
* zyd_cb_lock: a special signal structure that is used for notification
* that a callback function has been called.
*/
/* Initializes the zyd_cb_lock structure. */
void
{
}
/* Deinitalizes the zyd_cb_lock structure. */
void
{
}
/*
* Wait on lock until someone calls the "signal" function or the timeout
* expires. Note: timeout is in microseconds.
*/
{
int cv_res;
if (timeout < 0) {
/* no timeout - wait as long as needed */
} else {
/* wait with timeout (given in usec) */
cv_res =
if (cv_res <= 0)
break;
}
}
return (res);
}
/* Signal that the job (eg. callback) is done and unblock anyone who waits. */
void
{
}
/*
* 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);
}