uwba.c revision ff0e937b36dcde1a47ff7b00aa76a491c0dc07a8
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file is for uwba private functions
*/
static kmutex_t uwba_mutex;
/* list for uwba_dev, the radio host devices */
static list_t uwba_dev_list;
/* modload support */
extern struct mod_ops mod_miscops;
&mod_miscops, /* Type of module */
"UWBA: UWB Architecture"
};
static struct modlinkage modlinkage = {
};
/* This table is for data decode */
[UWB_NOTIF_IE_RECEIVED] = {
.buf_len_offset = 6
},
.struct_len = sizeof (uwb_rceb_beacon_t),
},
.struct_len = sizeof (uwb_rceb_beacon_size_change_t),
},
[UWB_NOTIF_BPOIE_CHANGE] = {
.struct_len = sizeof (uwb_rceb_bpoie_change_t),
},
[UWB_NOTIF_BP_SLOT_CHANGE] = {
.struct_len = sizeof (uwb_rceb_bp_slot_change_t),
},
},
},
.struct_len = sizeof (uwb_rceb_drp_availability_t),
},
[UWB_NOTIF_DRP] = {
.struct_len = sizeof (uwb_rceb_drp_t),
.buf_len_offset = 8
},
},
[UWB_NOTIF_CMD_FRAME_RCV] = {
},
},
[UWB_NOTIF_RESERVED] = {
},
[UWB_NOTIF_RESERVED + 1] = {
},
[UWB_NOTIF_RESERVED + 2] = {
},
[UWB_NOTIF_RESERVED + 3] = {
},
[UWB_CE_CHANNEL_CHANGE] = {
.struct_len = sizeof (uwb_rceb_head_t),
},
[UWB_CE_DEV_ADDR_MGMT] = {
.struct_len = sizeof (uwb_rceb_dev_addr_mgmt_t),
},
[UWB_CE_GET_IE] = {
.struct_len = sizeof (uwb_rceb_get_ie_t),
.buf_len_offset = 4
},
[UWB_CE_RESET] = {
},
[UWB_CE_SCAN] = {
},
[UWB_CE_SET_BEACON_FILTER] = {
},
[UWB_CE_SET_DRP_IE] = {
.struct_len = sizeof (uwb_rceb_set_drp_ie_t),
},
[UWB_CE_SET_IE] = {
.struct_len = sizeof (uwb_rceb_set_ie_t),
},
},
[UWB_CE_SET_TX_POWER] = {
},
[UWB_CE_SLEEP] = {
},
[UWB_CE_START_BEACON] = {
},
[UWB_CE_STOP_BEACON] = {
},
[UWB_CE_BP_MERGE] = {
},
},
},
};
/* This table is used for debug only */
const char *uwba_evt_msg_table[] = {
[UWB_NOTIF_IE_RECEIVED] = "UWB_NOTIF_IE_RECEIVED",
[UWB_NOTIF_BEACON_RECEIVED] = "UWB_NOTIF_BEACON_RECEIVED",
[UWB_NOTIF_BEACON_SIZE_CHANGE] = "UWB_NOTIF_BEACON_SIZE_CHANGE",
[UWB_NOTIF_BPOIE_CHANGE] = "UWB_NOTIF_BPOIE_CHANGE",
[UWB_NOTIF_BP_SLOT_CHANGE] = "UWB_NOTIF_BP_SLOT_CHANGE",
[UWB_NOTIF_BP_SWITCH_IE_RECEIVED] = "UWB_NOTIF_BP_SWITCH_IE_RECEIVED",
[UWB_NOTIF_DEV_ADDR_CONFLICT] = "UWB_NOTIF_DEV_ADDR_CONFLICT",
"UWB_NOTIF_DRP_AVAILABILITY_CHANGE",
[UWB_NOTIF_DRP] = "UWB_NOTIF_DRP",
[UWB_NOTIF_BP_SWITCH_STATUS] = "UWB_NOTIF_BP_SWITCH_STATUS",
[UWB_NOTIF_CMD_FRAME_RCV] = "UWB_NOTIF_CMD_FRAME_RCV",
[UWB_NOTIF_CHANNEL_CHANGE_IE_RCV] = "UWB_NOTIF_CHANNEL_CHANGE_IE_RCV",
[UWB_NOTIF_RESERVED] = "UWB_NOTIF_RESERVED",
[UWB_CE_CHANNEL_CHANGE] = "UWB_CE_CHANNEL_CHANGE",
[UWB_CE_DEV_ADDR_MGMT] = "UWB_CE_DEV_ADDR_MGMT",
[UWB_CE_GET_IE] = "UWB_CE_GET_IE",
[UWB_CE_RESET] = "UWB_CE_RESET",
[UWB_CE_SCAN] = "UWB_CE_SCAN",
[UWB_CE_SET_BEACON_FILTER] = "UWB_CE_SET_BEACON_FILTER",
[UWB_CE_SET_DRP_IE] = "UWB_CE_SET_DRP_IE",
[UWB_CE_SET_IE] = "UWB_CE_SET_IE",
[UWB_CE_SET_NOTIFICATION_FILTER] = "UWB_CE_SET_NOTIFICATION_FILTER",
[UWB_CE_SET_TX_POWER] = "UWB_CE_SET_TX_POWER",
[UWB_CE_SLEEP] = "UWB_CE_SLEEP",
[UWB_CE_START_BEACON] = "UWB_CE_START_BEACON",
[UWB_CE_STOP_BEACON] = "UWB_CE_STOP_BEACON",
[UWB_CE_BP_MERGE] = "UWB_CE_BP_MERGE",
[UWB_CE_SEND_COMMAND_FRAME] = "UWB_CE_SEND_COMMAND_FRAME",
[UWB_CE_SET_ASIE_NOTIFICATION] = "UWB_CE_SET_ASIE_NOTIFICATION",
};
static void uwba_init_lists(void);
static void uwba_fini_lists(void);
static void uwba_remove_cdev_list(uwba_dev_t *);
static void uwba_list_phy_rates(uwb_dev_handle_t);
static void uwba_list_phy_bandgroups(uwb_dev_handle_t);
int
_init(void)
{
int rval;
/*
* uwba providing uwb device list support needs to be init'ed first
* and destroyed last
*/
}
return (rval);
}
int
_fini()
{
int rval;
}
return (rval);
}
int
{
}
/* Create the global uwb dev list */
static void
uwba_init_lists(void)
{
}
/* Destroy the global uwb dev list */
static void
uwba_fini_lists(void)
{
/* Free all uwb dev node from dev_list */
while (!list_is_empty(&uwba_dev_list)) {
}
}
}
{
goto done;
}
}
done:
return (uwb_dev_handle_t)(uwba_dev);
}
void
{
"add uwba_dev = %x", uwba_dev);
}
void
{
if (list_is_empty(&uwba_dev_list)) {
return;
}
"remove uwba_dev = %x", uwba_dev);
}
void
{
}
void
{
/* bitset_fini will free the mem allocated by bitset_resize. */
}
/* Get a free context id from bitset */
{
/* if reaches the top, turn around */
}
/* Search ctxt_id+1 to UWB_CTXT_ID_UNVALID */
do {
ctxt_id++;
/* test bit and returen if it is not set */
return (ctxt_id);
}
} while (ctxt_id < UWB_CTXT_ID_UNVALID);
/* Search 1 to ctxt_id */
do {
ctxt_id++;
/* test bit and returen if it is not set */
return (ctxt_id);
}
}
/* All ids are in use, just force to re-use one. */
}
/* Reset the bit (offset at ctxt_id) to zero */
void
{
}
/* Fill the rccb to ctrl req's data block */
void
{
}
/*
* Allocate uwb_dev_t for a radio controller device. Arg rcd_intr_pri is the
* interrupt priority of the interrupt handler of the radio controller driver.
* If there is no interrupt handler in the driver, then pass 0 to this arg.
*/
void
{
char *devinst;
int devinstlen;
/*
* HWA radio controller will not call uwb_* functions in interrupt
* level, while WHCI radio controller will.
*/
if (rcd_intr_pri == 0) {
} else {
}
/* create a string for driver name and instance number */
/* list to cache the notifications from radio controller device */
/* list to record the client devices according to beacons received */
(*uwba_dev)->client_dev_cnt = 0;
}
void
{
/*
* remove all the notifications in this device's list, and then destroy
* the list
*/
} else {
break;
}
/* Free notification struct */
}
}
}
/* Destroy mutex and dev structure */
}
/* Get a event or notification code from the data stream */
{
/*
* UWB_RAW_RESULT_CODE_SIZE is the minimum size for any events or
* notifications.
*/
if (data_len < UWB_RAW_RESULT_CODE_SIZE) {
"uwba_get_evt_code: invalid data_len=%d",
data_len);
return (UWB_INVALID_EVT_CODE);
}
/* if out of range */
if (evt_code > UWB_CE_SET_ASIE_NOTIFICATION) {
"uwba_get_evt_code: invalid evt_code=%d",
evt_code);
return (UWB_INVALID_EVT_CODE);
}
/* if fall into the reserved range */
if (evt_code >= UWB_NOTIF_RESERVED &&
"uwba_get_evt_code: reserved evt_code=%d", evt_code);
return (UWB_INVALID_EVT_CODE);
}
return (evt_code);
}
{
/* If the offset of the variable data length is out of range. */
if (buf_len_off >= data_len) {
return (UWB_INVALID_EVT_SIZE);
}
/* If this event has variable length data, add up the length. */
if (buf_len_off) {
/* in case buf_len is not a reasonable value. */
"uwba_get_evt_size: data_len=%d, buf_len_off=%d,"
return (UWB_INVALID_EVT_SIZE);
}
/*
* after add up, the evt size may be a couple of bytes greater
* (depends on the structure alignment) than we actually need,
* but it does not matter.
*/
}
/*
* TODO: check if data_len is less than expected raw event data, for the
*/
return (evt_size);
}
/*
* hook the new event to uwb device handle, replace the old one if there is.
* Signal the cmd thread which is waiting this cmd result.
*/
void
{
"previous command result not processed "
"bEventType = %d, wEvent = %d, ctxt_id = %d",
}
"uwba_put_cmd_result: wEvent= %d, msg= %s",
} else {
"the cmd result ctxt_id %d is not matching the"
"current cmd ctxt_id %d",
}
}
/* add the notification to the tail of uwba_dev->notif_list */
int
{
"uwba_add_notif_to_list: allocate notif wrapper failed");
return (UWB_NO_RESOURCES);
}
/* remove oldest one */
/* Free it */
}
}
"uwba_add_notif_to_list: notification code=%d, notif_cnt=%d",
return (UWB_SUCCESS);
}
/*
* find the specific client device in the client_dev_list by comparing the MAC
* address
*/
{
sizeof (uwb_mac_addr_t)) == 0) {
return (cdev);
}
}
return (cdev);
}
/* find the client device beconing in a specific channel */
{
return (cdev);
}
}
return (cdev);
}
/* remove all cdev list for uwb dev */
static void
{
} else {
break;
}
}
}
/* add a client radio device to the tail of uwba_dev->client_dev_list */
int
{
int rval = UWB_SUCCESS;
"uwba_add_cdev_to_list: can not add this dev,"
"client dev number reached max, client_dev_cnt=%d",
rval = UWB_FAILURE;
goto done;
}
"uwba_add_cdev_to_list: this client dev is added before");
rval = UWB_SUCCESS;
goto done;
}
"uwba_add_client_device: allocate "
"uwba_client_dev_t failed");
goto done;
}
"uwba_add_cdev_to_list: a new client dev added. MAC: "
"%x %x %x %x %x %x, client_dev_cnt=%d",
done:
return (rval);
}
/*
* Return the actual parsed raw data length. Stop parse if datalen or structlen
* is out of range, and then return UWB_PARSE_ERROR.
*/
int
uwba_parse_data(char *format,
void *structure,
{
int fmt;
int counter = 1;
int multiplier = 0;
return (UWB_PARSE_ERROR);
}
/*
* Could some one pass a "format" that is greater than
* the structlen? Conversely, one could pass a ret_buf_len
* that is less than the "format" length.
* If so, we need to protect against writing over memory.
*/
return (UWB_PARSE_ERROR);
}
if (fmt == 'c') {
~(_CHAR_ALIGNMENT - 1));
/*
* If data or structure is out of range, stop parse.
*/
return (UWB_PARSE_ERROR);
if (multiplier) {
multiplier--;
}
if (multiplier == 0) {
format++;
}
} else if (fmt == 's') {
~(_SHORT_ALIGNMENT - 1));
return (UWB_PARSE_ERROR);
data += 2;
if (multiplier) {
multiplier--;
}
if (multiplier == 0) {
format++;
}
format++;
counter--;
} else {
multiplier = 0;
return (UWB_PARSE_ERROR);
}
}
}
/*
* parse rceb, check if the context id is in the reasonable range (0x0 - 0xfe).
* If success, return the offset just after the rceb struct.
*/
int
void *structure,
{
int parsed_len;
if (parsed_len == UWB_PARSE_ERROR) {
return (UWB_PARSE_ERROR);
}
return (UWB_PARSE_ERROR);
}
return (parsed_len);
}
int
{
/* 6 bytes for address, 1 for result code. */
return (UWB_PARSE_ERROR);
}
return (UWB_SUCCESS);
}
/* Parse UWB IEs that got by get_ie radio controller command */
int
{
int i;
/* At least, it should have wIELength. */
if (spec_data_len < 2) {
return (UWB_PARSE_ERROR);
}
/*
* Except wIELength, it should have the number of bytes of indicated by
* wIELength.
*/
return (UWB_PARSE_ERROR);
}
/*
* Proper memory for evt_struct is already allocated for evt_struct in
* uwb_parse_evt_notif()
*/
for (i = 0; i < evt_struct->wIELength; i++) {
}
/* Todo: continue to parse other IE Data? */
return (UWB_SUCCESS);
}
/*
* Parse the beacon frame and add the client radio device to the dev_list in
* uwb_dev_handle
*/
void
{
/*
* add the uwb device to list if it is a newly found device according to
* its MAC addr in the beacon
*/
/* TODO: log messages */
return;
}
}
int
int parsed_len;
if (parsed_len == UWB_PARSE_ERROR) {
"uwba_parse_bpoie_chg: parse error, parsed_len=%d",
return (UWB_PARSE_ERROR);
}
/* Todo: not supported now */
return (UWB_SUCCESS);
}
/* Parse the beacon_receive notif */
int
{
int parsed_len;
/* parse the elements except BeaconInfo */
if (parsed_len == UWB_PARSE_ERROR) {
"uwba_parse_beacon_rcv: parse error, parsed_len=%d",
return (UWB_PARSE_ERROR);
}
/*
* Except the elements before BeaconInfo, it should have the number of
* bytes of indicated by wBeaconInfoLength.
*/
if ((spec_data_len -UWB_BEACONINFO_OFFSET) <
"uwba_parse_beacon_rcv: parse error: spec_data_len=%d,"
"evt_struct->wBeaconInfoLength=%d, bc_info_offset=%d",
return (UWB_PARSE_ERROR);
}
"uwba_parse_beacon_rcv: too small size, "
"wBeaconInfoLength=%d, data[0]=%d, data[1]=%d, data[4]=%d,"
" data[5]=%d, data[6]=%d, data[7]=%d, spec_data_len=%d",
return (UWB_PARSE_ERROR);
}
"bChannelNumber = %d bBeaconType = %d "
"wBPSTOffset = %d bLQI = %d bRSSI = %d "
"wBeaconInfoLength = %d",
/*
* Proper memory for evt_struct is already allocated for evt_struct in
* uwb_parse_evt_notif()
*/
/*
* Parse the beacon frame and add the client radio device
* to the dev_list in uwb_dev_handle
*/
&(bc_frm->Device_Identifier));
/* Update the client device's beconing information */
return (UWB_SUCCESS);
}
/*
* find the phy capability ie from ie_data, then save the capability bitmap to
* uwb_dev_hdl
*/
static void
{
/* traverse ie_data to find PHY Capabilities IE */
return;
}
/* copy the phy capabilities bitmap to uwba_dev->phy_cap_bm */
}
/* Copy the PHY capability bitmap from phy_ie to uwb device handle */
static void
{
uwba_dev->phy_cap_bm = 0;
}
/* List all supported PHY data rates by checking the PHY capability bitmap */
static void
{
int i;
const char *uwb_phy_rate_table[] = {
};
for (i = UWB_RATE_OFFSET_BASE; i <= UWB_RATE_OFFSET_480; i++) {
"uwba_list_phy_rates: Rate supported=%s",
}
}
"uwba_list_phy_rates: phy_cap_bm=%u",
}
/* List all supported PHY band groups by checking the PHY capability bitmap */
static void
{
int i;
/* group 1 to 4 [ECMA, Table 112] */
for (i = 0; i <= 7; i++) {
"uwba_list_phy_bandgroups: band groups "
"supported=%d", i);
}
}
/* group 5 [ECMA, Table 112] */
"uwba_list_phy_bandgroups: band groups supported=%d", i);
}
}
/*
* Allocate a channel for the HC. Scan every channel supported,
* if beacon information from the channel received, scan the next
* channel, or else, stop.
* Return:
* first channel with no beacon recieved
* last channel if every channel is busy
* 0 if scan failure
* uwba_channel_table is used to decode the PHY capability IE.
* The array index is the bit in the PHY IE. base is the first
* TFC code.offset is the length from the first TFC code.
* Refer to:
* [ECM-368]. CHAP 11.2. Table 25-30.
* [ECM-368]. CHAP 16.8.16. Table 112
*
*/
int i, j;
};
int tbl_size = sizeof (uwba_channel_table) /
sizeof (uwba_channel_range_t);
for (i = 0; i < tbl_size; i++) {
for (j = 0; j < uwba_channel_table[i].offset; j++) {
!= UWB_SUCCESS) {
"uwba_allocate_channel: scan chanel"
" %d failed", channel);
return (0);
}
/* No beacon recevied in this channel, return */
return (channel);
}
"uwba_allocate_channel: exsting device becaoning"
"in channel = %d ", channel);
}
}
/*
* when we reach here, it means all the channel has beconning device,
* return the last channel
*/
done:
"uwba_allocate_channel: return channel = %d",
channel);
return (channel);
}
/*
* Find a IE with a specific ID in ie_data. Return the pointer to the IE head if
* found, else return NULL.
*/
uint8_t *
{
int i = 0;
while (i < (ie_len - 1)) {
break;
}
i++; /* move to the length item of the current IE */
curr_ie_len = ie_data[i];
}
if (matched) {
/*
* if the rest ie data are less than that indicated in the
* matched IE's length, then this is not valid IE
*/
"the matched IE is not valid. "
"curr_ie_len=%d, i=%d", curr_ie_len, i);
return (NULL);
}
return (&ie_data[i]);
}
return (NULL);
}
void
{
}
/* uwba_log, log the message output to dmesg according to err level */
void
{
if (msglevel <= uwba_errlevel) {
char *format;
int devinst_start = 0;
if (uwba_dev) {
} else {
devinst = "uwba: ";
}
/* Allocate extra room if driver name and instance is present */
if (msglevel >= UWBA_LOG_LOG) {
format[0] = '!';
devinst_start = 1;
}
}
}
/* Get a msg string of a event or notfication */
const char *
if (wEvent > UWB_CE_SET_ASIE_NOTIFICATION) {
return ("Unknown Message");
} else {
return (uwba_evt_msg_table[wEvent]);
}
}