diff options
| -rw-r--r-- | drivers/usb/wusbcore/reservation.c | 13 | ||||
| -rw-r--r-- | drivers/uwb/Makefile | 1 | ||||
| -rw-r--r-- | drivers/uwb/allocator.c | 386 | ||||
| -rw-r--r-- | drivers/uwb/drp-avail.c | 4 | ||||
| -rw-r--r-- | drivers/uwb/drp-ie.c | 160 | ||||
| -rw-r--r-- | drivers/uwb/drp.c | 681 | ||||
| -rw-r--r-- | drivers/uwb/rsv.c | 482 | ||||
| -rw-r--r-- | drivers/uwb/uwb-debug.c | 49 | ||||
| -rw-r--r-- | drivers/uwb/uwb-internal.h | 80 | ||||
| -rw-r--r-- | include/linux/uwb.h | 47 | ||||
| -rw-r--r-- | include/linux/uwb/debug-cmd.h | 2 | ||||
| -rw-r--r-- | include/linux/uwb/spec.h | 25 | 
12 files changed, 1603 insertions, 327 deletions
diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c index 7b6525dac2f..c37e4f83e54 100644 --- a/drivers/usb/wusbcore/reservation.c +++ b/drivers/usb/wusbcore/reservation.c @@ -48,13 +48,15 @@ static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv)  {  	struct wusbhc *wusbhc = rsv->pal_priv;  	struct device *dev = wusbhc->dev; +	struct uwb_mas_bm mas;  	char buf[72];  	switch (rsv->state) {  	case UWB_RSV_STATE_O_ESTABLISHED: -		bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS); +		uwb_rsv_get_usable_mas(rsv, &mas); +		bitmap_scnprintf(buf, sizeof(buf), mas.bm, UWB_NUM_MAS);  		dev_dbg(dev, "established reservation: %s\n", buf); -		wusbhc_bwa_set(wusbhc, rsv->stream, &rsv->mas); +		wusbhc_bwa_set(wusbhc, rsv->stream, &mas);  		break;  	case UWB_RSV_STATE_NONE:  		dev_dbg(dev, "removed reservation\n"); @@ -85,13 +87,12 @@ int wusbhc_rsv_establish(struct wusbhc *wusbhc)  	bcid.data[0] = wusbhc->cluster_id;  	bcid.data[1] = 0; -	rsv->owner = &rc->uwb_dev;  	rsv->target.type = UWB_RSV_TARGET_DEVADDR;  	rsv->target.devaddr = bcid;  	rsv->type = UWB_DRP_TYPE_PRIVATE; -	rsv->max_mas = 256; -	rsv->min_mas = 16;  /* one MAS per zone? */ -	rsv->sparsity = 16; /* at least one MAS in each zone? */ +	rsv->max_mas = 256; /* try to get as much as possible */ +	rsv->min_mas = 15;  /* one MAS per zone */ +	rsv->max_interval = 1; /* max latency is one zone */  	rsv->is_multicast = true;  	ret = uwb_rsv_establish(rsv); diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile index ce21a95da04..2f98d080fe7 100644 --- a/drivers/uwb/Makefile +++ b/drivers/uwb/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_UWB_I1480U)	+= i1480/  uwb-objs :=		\  	address.o	\ +	allocator.o	\  	beacon.o	\  	driver.o	\  	drp.o		\ diff --git a/drivers/uwb/allocator.c b/drivers/uwb/allocator.c new file mode 100644 index 00000000000..c8185e6b0cd --- /dev/null +++ b/drivers/uwb/allocator.c @@ -0,0 +1,386 @@ +/* + * UWB reservation management. + * + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/uwb.h> + +#include "uwb-internal.h" + +static void uwb_rsv_fill_column_alloc(struct uwb_rsv_alloc_info *ai) +{ +	int col, mas, safe_mas, unsafe_mas; +	unsigned char *bm = ai->bm; +	struct uwb_rsv_col_info *ci = ai->ci; +	unsigned char c; + +	for (col = ci->csi.start_col; col < UWB_NUM_ZONES; col += ci->csi.interval) { +     +		safe_mas   = ci->csi.safe_mas_per_col; +		unsafe_mas = ci->csi.unsafe_mas_per_col; +     +		for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++ ) { +			if (bm[col * UWB_MAS_PER_ZONE + mas] == 0) { +	 +				if (safe_mas > 0) { +					safe_mas--; +					c = UWB_RSV_MAS_SAFE; +				} else if (unsafe_mas > 0) { +					unsafe_mas--; +					c = UWB_RSV_MAS_UNSAFE; +				} else { +					break; +				} +				bm[col * UWB_MAS_PER_ZONE + mas] = c; +			} +		} +	} +} + +static void uwb_rsv_fill_row_alloc(struct uwb_rsv_alloc_info *ai) +{ +	int mas, col, rows; +	unsigned char *bm = ai->bm; +	struct uwb_rsv_row_info *ri = &ai->ri; +	unsigned char c; + +	rows = 1; +	c = UWB_RSV_MAS_SAFE; +	for (mas = UWB_MAS_PER_ZONE - 1; mas >= 0; mas--) { +		if (ri->avail[mas] == 1) { +       +			if (rows > ri->used_rows) { +				break; +			} else if (rows > 7) { +				c = UWB_RSV_MAS_UNSAFE; +			} + +			for (col = 0; col < UWB_NUM_ZONES; col++) { +				if (bm[col * UWB_NUM_ZONES + mas] != UWB_RSV_MAS_NOT_AVAIL) { +					bm[col * UWB_NUM_ZONES + mas] = c; +					if(c == UWB_RSV_MAS_SAFE) +						ai->safe_allocated_mases++; +					else +						ai->unsafe_allocated_mases++; +				} +			} +			rows++; +		} +	} +	ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases; +} + +/* + * Find the best column set for a given availability, interval, num safe mas and + * num unsafe mas. + * + * The different sets are tried in order as shown below, depending on the interval. + * + * interval = 16 + *	deep = 0 + *		set 1 ->  {  8 } + *	deep = 1 + *		set 1 ->  {  4 } + *		set 2 ->  { 12 } + *	deep = 2 + *		set 1 ->  {  2 } + *		set 2 ->  {  6 } + *		set 3 ->  { 10 } + *		set 4 ->  { 14 } + *	deep = 3 + *		set 1 ->  {  1 } + *		set 2 ->  {  3 } + *		set 3 ->  {  5 } + *		set 4 ->  {  7 } + *		set 5 ->  {  9 } + *		set 6 ->  { 11 } + *		set 7 ->  { 13 } + *		set 8 ->  { 15 } + * + * interval = 8 + *	deep = 0 + *		set 1 ->  {  4  12 } + *	deep = 1 + *		set 1 ->  {  2  10 } + *		set 2 ->  {  6  14 } + *	deep = 2 + *		set 1 ->  {  1   9 } + *		set 2 ->  {  3  11 } + *		set 3 ->  {  5  13 } + *		set 4 ->  {  7  15 } + * + * interval = 4 + *	deep = 0 + *		set 1 ->  {  2   6  10  14 } + *	deep = 1 + *		set 1 ->  {  1   5   9  13 } + *		set 2 ->  {  3   7  11  15 } + * + * interval = 2 + *	deep = 0 + *		set 1 ->  {  1   3   5   7   9  11  13  15 } + */ +static int uwb_rsv_find_best_column_set(struct uwb_rsv_alloc_info *ai, int interval,  +					int num_safe_mas, int num_unsafe_mas) +{ +	struct uwb_rsv_col_info *ci = ai->ci; +	struct uwb_rsv_col_set_info *csi = &ci->csi; +	struct uwb_rsv_col_set_info tmp_csi; +	int deep, set, col, start_col_deep, col_start_set; +	int start_col, max_mas_in_set, lowest_max_mas_in_deep; +	int n_mas; +	int found = UWB_RSV_ALLOC_NOT_FOUND;  + +	tmp_csi.start_col = 0; +	start_col_deep = interval; +	n_mas = num_unsafe_mas + num_safe_mas; + +	for (deep = 0; ((interval >> deep) & 0x1) == 0; deep++) { +		start_col_deep /= 2; +		col_start_set = 0; +		lowest_max_mas_in_deep = UWB_MAS_PER_ZONE; + +		for (set = 1; set <= (1 << deep); set++) { +			max_mas_in_set = 0; +			start_col = start_col_deep + col_start_set; +			for (col = start_col; col < UWB_NUM_ZONES; col += interval) { +                 +				if (ci[col].max_avail_safe >= num_safe_mas && +				    ci[col].max_avail_unsafe >= n_mas) { +					if (ci[col].highest_mas[n_mas] > max_mas_in_set) +						max_mas_in_set = ci[col].highest_mas[n_mas]; +				} else { +					max_mas_in_set = 0; +					break; +				} +			} +			if ((lowest_max_mas_in_deep > max_mas_in_set) && max_mas_in_set) { +				lowest_max_mas_in_deep = max_mas_in_set; + +				tmp_csi.start_col = start_col; +			} +			col_start_set += (interval >> deep); +		} + +		if (lowest_max_mas_in_deep < 8) { +			csi->start_col = tmp_csi.start_col; +			found = UWB_RSV_ALLOC_FOUND; +			break; +		} else if ((lowest_max_mas_in_deep > 8) &&  +			   (lowest_max_mas_in_deep != UWB_MAS_PER_ZONE) && +			   (found == UWB_RSV_ALLOC_NOT_FOUND)) { +			csi->start_col = tmp_csi.start_col; +			found = UWB_RSV_ALLOC_FOUND; +		} +	} + +	if (found == UWB_RSV_ALLOC_FOUND) { +		csi->interval = interval; +		csi->safe_mas_per_col = num_safe_mas; +		csi->unsafe_mas_per_col = num_unsafe_mas; + +		ai->safe_allocated_mases = (UWB_NUM_ZONES / interval) * num_safe_mas; +		ai->unsafe_allocated_mases = (UWB_NUM_ZONES / interval) * num_unsafe_mas; +		ai->total_allocated_mases = ai->safe_allocated_mases + ai->unsafe_allocated_mases; +		ai->interval = interval;		 +	} +	return found; +} + +static void get_row_descriptors(struct uwb_rsv_alloc_info *ai) +{ +	unsigned char *bm = ai->bm; +	struct uwb_rsv_row_info *ri = &ai->ri; +	int col, mas; +   +	ri->free_rows = 16; +	for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) { +		ri->avail[mas] = 1; +		for (col = 1; col < UWB_NUM_ZONES; col++) { +			if (bm[col * UWB_NUM_ZONES + mas] == UWB_RSV_MAS_NOT_AVAIL) { +				ri->free_rows--; +				ri->avail[mas]=0; +				break; +			} +		} +	} +} + +static void uwb_rsv_fill_column_info(unsigned char *bm, int column, struct uwb_rsv_col_info *rci) +{ +	int mas; +	int block_count = 0, start_block = 0;  +	int previous_avail = 0; +	int available = 0; +	int safe_mas_in_row[UWB_MAS_PER_ZONE] = { +		8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, +	}; + +	rci->max_avail_safe = 0; + +	for (mas = 0; mas < UWB_MAS_PER_ZONE; mas ++) { +		if (!bm[column * UWB_NUM_ZONES + mas]) { +			available++; +			rci->max_avail_unsafe = available; + +			rci->highest_mas[available] = mas; + +			if (previous_avail) { +				block_count++; +				if ((block_count > safe_mas_in_row[start_block]) && +				    (!rci->max_avail_safe)) +					rci->max_avail_safe = available - 1; +			} else { +				previous_avail = 1; +				start_block = mas; +				block_count = 1; +			} +		} else { +			previous_avail = 0; +		} +	} +	if (!rci->max_avail_safe) +		rci->max_avail_safe = rci->max_avail_unsafe; +} + +static void get_column_descriptors(struct uwb_rsv_alloc_info *ai) +{ +	unsigned char *bm = ai->bm; +	struct uwb_rsv_col_info *ci = ai->ci; +	int col; + +	for (col = 1; col < UWB_NUM_ZONES; col++) { +		uwb_rsv_fill_column_info(bm, col, &ci[col]); +	} +} + +static int uwb_rsv_find_best_row_alloc(struct uwb_rsv_alloc_info *ai) +{ +	int n_rows; +	int max_rows = ai->max_mas / UWB_USABLE_MAS_PER_ROW; +	int min_rows = ai->min_mas / UWB_USABLE_MAS_PER_ROW; +	if (ai->min_mas % UWB_USABLE_MAS_PER_ROW) +		min_rows++; +	for (n_rows = max_rows; n_rows >= min_rows; n_rows--) { +		if (n_rows <= ai->ri.free_rows) { +			ai->ri.used_rows = n_rows; +			ai->interval = 1; /* row reservation */ +			uwb_rsv_fill_row_alloc(ai); +			return UWB_RSV_ALLOC_FOUND; +		} +	}   +	return UWB_RSV_ALLOC_NOT_FOUND; +} + +static int uwb_rsv_find_best_col_alloc(struct uwb_rsv_alloc_info *ai, int interval) +{ +	int n_safe, n_unsafe, n_mas;   +	int n_column = UWB_NUM_ZONES / interval; +	int max_per_zone = ai->max_mas / n_column; +	int min_per_zone = ai->min_mas / n_column; + +	if (ai->min_mas % n_column) +		min_per_zone++; + +	if (min_per_zone > UWB_MAS_PER_ZONE) { +		return UWB_RSV_ALLOC_NOT_FOUND; +	} +     +	if (max_per_zone > UWB_MAS_PER_ZONE) { +		max_per_zone = UWB_MAS_PER_ZONE; +	} +     +	for (n_mas = max_per_zone; n_mas >= min_per_zone; n_mas--) { +		if (uwb_rsv_find_best_column_set(ai, interval, 0, n_mas) == UWB_RSV_ALLOC_NOT_FOUND) +			continue; +		for (n_safe = n_mas; n_safe >= 0; n_safe--) { +			n_unsafe = n_mas - n_safe; +			if (uwb_rsv_find_best_column_set(ai, interval, n_safe, n_unsafe) == UWB_RSV_ALLOC_FOUND) { +				uwb_rsv_fill_column_alloc(ai); +				return UWB_RSV_ALLOC_FOUND; +			} +		} +	} +	return UWB_RSV_ALLOC_NOT_FOUND; +} + +int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *available,  +				 struct uwb_mas_bm *result) +{ +	struct uwb_rsv_alloc_info *ai; +	int interval; +	int bit_index; + +	ai = kzalloc(sizeof(struct uwb_rsv_alloc_info), GFP_KERNEL); +	 +	ai->min_mas = rsv->min_mas; +	ai->max_mas = rsv->max_mas; +	ai->max_interval = rsv->max_interval; + + +	/* fill the not available vector from the available bm */ +	for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) { +		if (!test_bit(bit_index, available->bm)) +			ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL; +	} + +	if (ai->max_interval == 1) { +		get_row_descriptors(ai); +		if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND) +			goto alloc_found; +		else +			goto alloc_not_found; +	} + +	get_column_descriptors(ai); +         +	for (interval = 16; interval >= 2; interval>>=1) { +		if (interval > ai->max_interval) +			continue; +		if (uwb_rsv_find_best_col_alloc(ai, interval) == UWB_RSV_ALLOC_FOUND) +			goto alloc_found; +	} + +	/* try row reservation if no column is found */ +	get_row_descriptors(ai); +	if (uwb_rsv_find_best_row_alloc(ai) == UWB_RSV_ALLOC_FOUND) +		goto alloc_found; +	else +		goto alloc_not_found; + +  alloc_found: +	bitmap_zero(result->bm, UWB_NUM_MAS); +	bitmap_zero(result->unsafe_bm, UWB_NUM_MAS); +	/* fill the safe and unsafe bitmaps */ +	for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) { +		if (ai->bm[bit_index] == UWB_RSV_MAS_SAFE) +			set_bit(bit_index, result->bm); +		else if (ai->bm[bit_index] == UWB_RSV_MAS_UNSAFE) +			set_bit(bit_index, result->unsafe_bm); +	} +	bitmap_or(result->bm, result->bm, result->unsafe_bm, UWB_NUM_MAS); + +	result->safe   = ai->safe_allocated_mases; +	result->unsafe = ai->unsafe_allocated_mases; +	 +	kfree(ai);		 +	return UWB_RSV_ALLOC_FOUND; +   +  alloc_not_found: +	kfree(ai); +	return UWB_RSV_ALLOC_NOT_FOUND; +} diff --git a/drivers/uwb/drp-avail.c b/drivers/uwb/drp-avail.c index 3febd855280..40a540a5a72 100644 --- a/drivers/uwb/drp-avail.c +++ b/drivers/uwb/drp-avail.c @@ -58,7 +58,7 @@ void uwb_drp_avail_init(struct uwb_rc *rc)   *   * avail = global & local & pending   */ -static void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail) +void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail)  {  	bitmap_and(avail->bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS);  	bitmap_and(avail->bm, avail->bm, rc->drp_avail.pending, UWB_NUM_MAS); @@ -105,6 +105,7 @@ void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas)  	bitmap_or(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS);  	bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);  	rc->drp_avail.ie_valid = false; +	uwb_rsv_handle_drp_avail_change(rc);  }  /** @@ -280,6 +281,7 @@ int uwbd_evt_handle_rc_drp_avail(struct uwb_event *evt)  	mutex_lock(&rc->rsvs_mutex);  	bitmap_copy(rc->drp_avail.global, bmp, UWB_NUM_MAS);  	rc->drp_avail.ie_valid = false; +	uwb_rsv_handle_drp_avail_change(rc);  	mutex_unlock(&rc->rsvs_mutex);  	uwb_rsv_sched_update(rc); diff --git a/drivers/uwb/drp-ie.c b/drivers/uwb/drp-ie.c index 75491d47806..2840d7bf9e6 100644 --- a/drivers/uwb/drp-ie.c +++ b/drivers/uwb/drp-ie.c @@ -22,6 +22,96 @@  #include "uwb-internal.h" + +/* + * Return the reason code for a reservations's DRP IE. + */ +int uwb_rsv_reason_code(struct uwb_rsv *rsv) +{ +	static const int reason_codes[] = { +		[UWB_RSV_STATE_O_INITIATED]          = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_O_PENDING]            = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_O_MODIFIED]           = UWB_DRP_REASON_MODIFIED, +		[UWB_RSV_STATE_O_ESTABLISHED]        = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_O_TO_BE_MOVED]        = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_O_MOVE_COMBINING]     = UWB_DRP_REASON_MODIFIED, +		[UWB_RSV_STATE_O_MOVE_REDUCING]      = UWB_DRP_REASON_MODIFIED, +		[UWB_RSV_STATE_O_MOVE_EXPANDING]     = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_T_ACCEPTED]           = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_T_CONFLICT]           = UWB_DRP_REASON_CONFLICT, +		[UWB_RSV_STATE_T_PENDING]            = UWB_DRP_REASON_PENDING, +		[UWB_RSV_STATE_T_DENIED]             = UWB_DRP_REASON_DENIED, +		[UWB_RSV_STATE_T_RESIZED]            = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT, +		[UWB_RSV_STATE_T_EXPANDING_PENDING]  = UWB_DRP_REASON_PENDING, +		[UWB_RSV_STATE_T_EXPANDING_DENIED]   = UWB_DRP_REASON_DENIED, +	}; + +	return reason_codes[rsv->state]; +} + +/* + * Return the reason code for a reservations's companion DRP IE . + */ +int uwb_rsv_companion_reason_code(struct uwb_rsv *rsv) +{ +	static const int companion_reason_codes[] = { +		[UWB_RSV_STATE_O_MOVE_EXPANDING]     = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = UWB_DRP_REASON_ACCEPTED, +		[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = UWB_DRP_REASON_CONFLICT, +		[UWB_RSV_STATE_T_EXPANDING_PENDING]  = UWB_DRP_REASON_PENDING, +		[UWB_RSV_STATE_T_EXPANDING_DENIED]   = UWB_DRP_REASON_DENIED, +	}; + +	return companion_reason_codes[rsv->state]; +} + +/* + * Return the status bit for a reservations's DRP IE. + */ +int uwb_rsv_status(struct uwb_rsv *rsv) +{ +	static const int statuses[] = { +		[UWB_RSV_STATE_O_INITIATED]          = 0, +		[UWB_RSV_STATE_O_PENDING]            = 0, +		[UWB_RSV_STATE_O_MODIFIED]           = 1, +		[UWB_RSV_STATE_O_ESTABLISHED]        = 1, +		[UWB_RSV_STATE_O_TO_BE_MOVED]        = 0, +		[UWB_RSV_STATE_O_MOVE_COMBINING]     = 1, +		[UWB_RSV_STATE_O_MOVE_REDUCING]      = 1, +		[UWB_RSV_STATE_O_MOVE_EXPANDING]     = 1, +		[UWB_RSV_STATE_T_ACCEPTED]           = 1, +		[UWB_RSV_STATE_T_CONFLICT]           = 0, +		[UWB_RSV_STATE_T_PENDING]            = 0, +		[UWB_RSV_STATE_T_DENIED]             = 0, +		[UWB_RSV_STATE_T_RESIZED]            = 1, +		[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1, +		[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 1, +		[UWB_RSV_STATE_T_EXPANDING_PENDING]  = 1, +		[UWB_RSV_STATE_T_EXPANDING_DENIED]   = 1, + +	}; + +	return statuses[rsv->state]; +} + +/* + * Return the status bit for a reservations's companion DRP IE . + */ +int uwb_rsv_companion_status(struct uwb_rsv *rsv) +{ +	static const int companion_statuses[] = { +		[UWB_RSV_STATE_O_MOVE_EXPANDING]     = 0, +		[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = 1, +		[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = 0, +		[UWB_RSV_STATE_T_EXPANDING_PENDING]  = 0, +		[UWB_RSV_STATE_T_EXPANDING_DENIED]   = 0, +	}; + +	return companion_statuses[rsv->state]; +} +  /*   * Allocate a DRP IE.   * @@ -33,16 +123,12 @@  static struct uwb_ie_drp *uwb_drp_ie_alloc(void)  {  	struct uwb_ie_drp *drp_ie; -	unsigned tiebreaker;  	drp_ie = kzalloc(sizeof(struct uwb_ie_drp) +  			UWB_NUM_ZONES * sizeof(struct uwb_drp_alloc),  			GFP_KERNEL);  	if (drp_ie) {  		drp_ie->hdr.element_id = UWB_IE_DRP; - -		get_random_bytes(&tiebreaker, sizeof(unsigned)); -		uwb_ie_drp_set_tiebreaker(drp_ie, tiebreaker & 1);  	}  	return drp_ie;  } @@ -103,43 +189,17 @@ static void uwb_drp_ie_from_bm(struct uwb_ie_drp *drp_ie,   */  int uwb_drp_ie_update(struct uwb_rsv *rsv)  { -	struct device *dev = &rsv->rc->uwb_dev.dev;  	struct uwb_ie_drp *drp_ie; -	int reason_code, status; +	struct uwb_rsv_move *mv; +	int unsafe; -	switch (rsv->state) { -	case UWB_RSV_STATE_NONE: +	if (rsv->state == UWB_RSV_STATE_NONE) {  		kfree(rsv->drp_ie);  		rsv->drp_ie = NULL;  		return 0; -	case UWB_RSV_STATE_O_INITIATED: -		reason_code = UWB_DRP_REASON_ACCEPTED; -		status = 0; -		break; -	case UWB_RSV_STATE_O_PENDING: -		reason_code = UWB_DRP_REASON_ACCEPTED; -		status = 0; -		break; -	case UWB_RSV_STATE_O_MODIFIED: -		reason_code = UWB_DRP_REASON_MODIFIED; -		status = 1; -		break; -	case UWB_RSV_STATE_O_ESTABLISHED: -		reason_code = UWB_DRP_REASON_ACCEPTED; -		status = 1; -		break; -	case UWB_RSV_STATE_T_ACCEPTED: -		reason_code = UWB_DRP_REASON_ACCEPTED; -		status = 1; -		break; -	case UWB_RSV_STATE_T_DENIED: -		reason_code = UWB_DRP_REASON_DENIED; -		status = 0; -		break; -	default: -		dev_dbg(dev, "rsv with unhandled state (%d)\n", rsv->state); -		return -EINVAL;  	} +	 +	unsafe = rsv->mas.unsafe ? 1 : 0;  	if (rsv->drp_ie == NULL) {  		rsv->drp_ie = uwb_drp_ie_alloc(); @@ -148,9 +208,11 @@ int uwb_drp_ie_update(struct uwb_rsv *rsv)  	}  	drp_ie = rsv->drp_ie; +	uwb_ie_drp_set_unsafe(drp_ie,       unsafe); +	uwb_ie_drp_set_tiebreaker(drp_ie,   rsv->tiebreaker);  	uwb_ie_drp_set_owner(drp_ie,        uwb_rsv_is_owner(rsv)); -	uwb_ie_drp_set_status(drp_ie,       status); -	uwb_ie_drp_set_reason_code(drp_ie,  reason_code); +	uwb_ie_drp_set_status(drp_ie,       uwb_rsv_status(rsv)); +	uwb_ie_drp_set_reason_code(drp_ie,  uwb_rsv_reason_code(rsv));  	uwb_ie_drp_set_stream_index(drp_ie, rsv->stream);  	uwb_ie_drp_set_type(drp_ie,         rsv->type); @@ -168,6 +230,27 @@ int uwb_drp_ie_update(struct uwb_rsv *rsv)  	uwb_drp_ie_from_bm(drp_ie, &rsv->mas); +	if (uwb_rsv_has_two_drp_ies(rsv)) { +		mv = &rsv->mv;  +		if (mv->companion_drp_ie == NULL) { +			mv->companion_drp_ie = uwb_drp_ie_alloc(); +			if (mv->companion_drp_ie == NULL) +				return -ENOMEM; +		} +		drp_ie = mv->companion_drp_ie; +		 +		/* keep all the same configuration of the main drp_ie */ +		memcpy(drp_ie, rsv->drp_ie, sizeof(struct uwb_ie_drp)); +		 + +		/* FIXME: handle properly the unsafe bit */ +		uwb_ie_drp_set_unsafe(drp_ie,       1); +		uwb_ie_drp_set_status(drp_ie,       uwb_rsv_companion_status(rsv)); +		uwb_ie_drp_set_reason_code(drp_ie,  uwb_rsv_companion_reason_code(rsv)); +	 +		uwb_drp_ie_from_bm(drp_ie, &mv->companion_mas); +	} +  	rsv->ie_valid = true;  	return 0;  } @@ -218,6 +301,8 @@ void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie)  	u8 zone;  	u16 zone_mask; +	bitmap_zero(bm->bm, UWB_NUM_MAS); +  	for (cnt = 0; cnt < numallocs; cnt++) {  		alloc = &drp_ie->allocs[cnt];  		zone_bm = le16_to_cpu(alloc->zone_bm); @@ -229,3 +314,4 @@ void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie)  		}  	}  } + diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c index fe328146adb..2b4f9406789 100644 --- a/drivers/uwb/drp.c +++ b/drivers/uwb/drp.c @@ -23,6 +23,59 @@  #include <linux/delay.h>  #include "uwb-internal.h" + +/* DRP Conflict Actions ([ECMA-368 2nd Edition] 17.4.6) */ +enum uwb_drp_conflict_action { +	/* Reservation is mantained, no action needed */ +	UWB_DRP_CONFLICT_MANTAIN = 0, +	 +	/* the device shall not transmit frames in conflicting MASs in +	 * the following superframe. If the device is the reservation +	 * target, it shall also set the Reason Code in its DRP IE to +	 * Conflict in its beacon in the following superframe. +	 */ +	UWB_DRP_CONFLICT_ACT1, +	 +	/* the device shall not set the Reservation Status bit to ONE +	 * and shall not transmit frames in conflicting MASs. If the +	 * device is the reservation target, it shall also set the +	 * Reason Code in its DRP IE to Conflict. +	 */	 +	UWB_DRP_CONFLICT_ACT2, + +	/* the device shall not transmit frames in conflicting MASs in +	 * the following superframe. It shall remove the conflicting +	 * MASs from the reservation or set the Reservation Status to +	 * ZERO in its beacon in the following superframe. If the +	 * device is the reservation target, it shall also set the +	 * Reason Code in its DRP IE to Conflict. +	 */ +	UWB_DRP_CONFLICT_ACT3, +}; + + +static void uwb_rc_set_drp_cmd_done(struct uwb_rc *rc, void *arg, +				    struct uwb_rceb *reply, ssize_t reply_size) +{ +	struct uwb_rc_evt_set_drp_ie *r = (struct uwb_rc_evt_set_drp_ie *)reply; + +	if (r != NULL) { +		if (r->bResultCode != UWB_RC_RES_SUCCESS) +			dev_err(&rc->uwb_dev.dev, "SET-DRP-IE failed: %s (%d)\n", +				uwb_rc_strerror(r->bResultCode), r->bResultCode); +	} else +		dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: timeout\n"); + +	spin_lock(&rc->rsvs_lock); +	if (rc->set_drp_ie_pending > 1) { +		rc->set_drp_ie_pending = 0; +		uwb_rsv_queue_update(rc);	 +	} else { +		rc->set_drp_ie_pending = 0;	 +	} +	spin_unlock(&rc->rsvs_lock); +} +  /**   * Construct and send the SET DRP IE   * @@ -46,18 +99,23 @@  int uwb_rc_send_all_drp_ie(struct uwb_rc *rc)  {  	int result; -	struct device *dev = &rc->uwb_dev.dev;  	struct uwb_rc_cmd_set_drp_ie *cmd; -	struct uwb_rc_evt_set_drp_ie reply;  	struct uwb_rsv *rsv; +	struct uwb_rsv_move *mv;  	int num_bytes = 0;  	u8 *IEDataptr;  	result = -ENOMEM;  	/* First traverse all reservations to determine memory needed. */  	list_for_each_entry(rsv, &rc->reservations, rc_node) { -		if (rsv->drp_ie != NULL) +		if (rsv->drp_ie != NULL) {  			num_bytes += rsv->drp_ie->hdr.length + 2; +			if (uwb_rsv_has_two_drp_ies(rsv) && +				(rsv->mv.companion_drp_ie != NULL)) { +				mv = &rsv->mv; +				num_bytes += mv->companion_drp_ie->hdr.length + 2;	 +			} +		}  	}  	num_bytes += sizeof(rc->drp_avail.ie);  	cmd = kzalloc(sizeof(*cmd) + num_bytes, GFP_KERNEL); @@ -68,109 +126,322 @@ int uwb_rc_send_all_drp_ie(struct uwb_rc *rc)  	cmd->wIELength = num_bytes;  	IEDataptr = (u8 *)&cmd->IEData[0]; +	/* FIXME: DRV avail IE is not always needed */ +	/* put DRP avail IE first */ +	memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie)); +	IEDataptr += sizeof(struct uwb_ie_drp_avail); +  	/* Next traverse all reservations to place IEs in allocated memory. */  	list_for_each_entry(rsv, &rc->reservations, rc_node) {  		if (rsv->drp_ie != NULL) {  			memcpy(IEDataptr, rsv->drp_ie,  			       rsv->drp_ie->hdr.length + 2);  			IEDataptr += rsv->drp_ie->hdr.length + 2; +			 +			if (uwb_rsv_has_two_drp_ies(rsv) && +				(rsv->mv.companion_drp_ie != NULL)) { +				mv = &rsv->mv; +				memcpy(IEDataptr, mv->companion_drp_ie, +				       mv->companion_drp_ie->hdr.length + 2); +				IEDataptr += mv->companion_drp_ie->hdr.length + 2;	 +			}  		}  	} -	memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie)); -	reply.rceb.bEventType = UWB_RC_CET_GENERAL; -	reply.rceb.wEvent = UWB_RC_CMD_SET_DRP_IE; -	result = uwb_rc_cmd(rc, "SET-DRP-IE", &cmd->rccb, -			sizeof(*cmd) + num_bytes, &reply.rceb, -			sizeof(reply)); -	if (result < 0) -		goto error_cmd; -	result = le16_to_cpu(reply.wRemainingSpace); -	if (reply.bResultCode != UWB_RC_RES_SUCCESS) { -		dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: command execution " -				"failed: %s (%d). RemainingSpace in beacon " -				"= %d\n", uwb_rc_strerror(reply.bResultCode), -				reply.bResultCode, result); -		result = -EIO; -	} else { -		dev_dbg(dev, "SET-DRP-IE sent. RemainingSpace in beacon " -			     "= %d.\n", result); -		result = 0; -	} -error_cmd: +	result = uwb_rc_cmd_async(rc, "SET-DRP-IE", &cmd->rccb, sizeof(*cmd) + num_bytes, +				  UWB_RC_CET_GENERAL, UWB_RC_CMD_SET_DRP_IE, +				  uwb_rc_set_drp_cmd_done, NULL); +	 +	rc->set_drp_ie_pending = 1; +  	kfree(cmd);  error:  	return result;  } -void uwb_drp_handle_timeout(struct uwb_rsv *rsv) +/* + * Evaluate the action to perform using conflict resolution rules + * + * Return a uwb_drp_conflict_action. + */ +static int evaluate_conflict_action(struct uwb_ie_drp *ext_drp_ie, int ext_beacon_slot, +				    struct uwb_rsv *rsv, int our_status)  { -	struct device *dev = &rsv->rc->uwb_dev.dev; +	int our_tie_breaker = rsv->tiebreaker; +	int our_type        = rsv->type; +	int our_beacon_slot = rsv->rc->uwb_dev.beacon_slot; -	dev_dbg(dev, "reservation timeout in state %s (%d)\n", -		uwb_rsv_state_str(rsv->state), rsv->state); +	int ext_tie_breaker = uwb_ie_drp_tiebreaker(ext_drp_ie); +	int ext_status      = uwb_ie_drp_status(ext_drp_ie); +	int ext_type        = uwb_ie_drp_type(ext_drp_ie); +	 +	 +	/* [ECMA-368 2nd Edition] 17.4.6 */ +	if (ext_type == UWB_DRP_TYPE_PCA && our_type == UWB_DRP_TYPE_PCA) { +		return UWB_DRP_CONFLICT_MANTAIN; +	} -	switch (rsv->state) { -	case UWB_RSV_STATE_O_INITIATED: -		if (rsv->is_multicast) { -			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); -			return; +	/* [ECMA-368 2nd Edition] 17.4.6-1 */ +	if (our_type == UWB_DRP_TYPE_ALIEN_BP) { +		return UWB_DRP_CONFLICT_MANTAIN; +	} +	 +	/* [ECMA-368 2nd Edition] 17.4.6-2 */ +	if (ext_type == UWB_DRP_TYPE_ALIEN_BP) { +		/* here we know our_type != UWB_DRP_TYPE_ALIEN_BP */ +		return UWB_DRP_CONFLICT_ACT1; +	} + +	/* [ECMA-368 2nd Edition] 17.4.6-3 */ +	if (our_status == 0 && ext_status == 1) { +		return UWB_DRP_CONFLICT_ACT2; +	} + +	/* [ECMA-368 2nd Edition] 17.4.6-4 */ +	if (our_status == 1 && ext_status == 0) { +		return UWB_DRP_CONFLICT_MANTAIN; +	} + +	/* [ECMA-368 2nd Edition] 17.4.6-5a */ +	if (our_tie_breaker == ext_tie_breaker && +	    our_beacon_slot <  ext_beacon_slot) { +		return UWB_DRP_CONFLICT_MANTAIN; +	} + +	/* [ECMA-368 2nd Edition] 17.4.6-5b */ +	if (our_tie_breaker != ext_tie_breaker && +	    our_beacon_slot >  ext_beacon_slot) { +		return UWB_DRP_CONFLICT_MANTAIN; +	} +	 +	if (our_status == 0) { +		if (our_tie_breaker == ext_tie_breaker) { +			/* [ECMA-368 2nd Edition] 17.4.6-6a */ +			if (our_beacon_slot > ext_beacon_slot) { +				return UWB_DRP_CONFLICT_ACT2; +			} +		} else  { +			/* [ECMA-368 2nd Edition] 17.4.6-6b */ +			if (our_beacon_slot < ext_beacon_slot) { +				return UWB_DRP_CONFLICT_ACT2; +			} +		} +	} else { +		if (our_tie_breaker == ext_tie_breaker) { +			/* [ECMA-368 2nd Edition] 17.4.6-7a */ +			if (our_beacon_slot > ext_beacon_slot) { +				return UWB_DRP_CONFLICT_ACT3; +			} +		} else { +			/* [ECMA-368 2nd Edition] 17.4.6-7b */ +			if (our_beacon_slot < ext_beacon_slot) { +				return UWB_DRP_CONFLICT_ACT3; +			}  		} -		break; -	case UWB_RSV_STATE_O_ESTABLISHED: -		if (rsv->is_multicast) -			return; -		break; -	default: -		break;  	} -	uwb_rsv_remove(rsv); +	return UWB_DRP_CONFLICT_MANTAIN;  } +static void handle_conflict_normal(struct uwb_ie_drp *drp_ie,  +				   int ext_beacon_slot,  +				   struct uwb_rsv *rsv,  +				   struct uwb_mas_bm *conflicting_mas) +{ +	struct uwb_rc *rc = rsv->rc; +	struct uwb_rsv_move *mv = &rsv->mv; +	struct uwb_drp_backoff_win *bow = &rc->bow; +	int action; + +	action = evaluate_conflict_action(drp_ie, ext_beacon_slot, rsv, uwb_rsv_status(rsv)); + +	if (uwb_rsv_is_owner(rsv)) { +		switch(action) { +		case UWB_DRP_CONFLICT_ACT2: +			/* try move */ +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_TO_BE_MOVED); +			if (bow->can_reserve_extra_mases == false) +				uwb_rsv_backoff_win_increment(rc); +			 +			break; +		case UWB_DRP_CONFLICT_ACT3: +			uwb_rsv_backoff_win_increment(rc); +			/* drop some mases with reason modified */ +			/* put in the companion the mases to be dropped */ +			bitmap_and(mv->companion_mas.bm, rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS); +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); +		default: +			break; +		} +	} else { +		switch(action) { +		case UWB_DRP_CONFLICT_ACT2: +		case UWB_DRP_CONFLICT_ACT3: +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT);	 +		default: +			break; +		} + +	} +	 +} + +static void handle_conflict_expanding(struct uwb_ie_drp *drp_ie, int ext_beacon_slot, +				      struct uwb_rsv *rsv, bool companion_only, +				      struct uwb_mas_bm *conflicting_mas) +{ +	struct uwb_rc *rc = rsv->rc; +	struct uwb_drp_backoff_win *bow = &rc->bow; +	struct uwb_rsv_move *mv = &rsv->mv; +	int action; +	 +	if (companion_only) { +		/* status of companion is 0 at this point */ +		action = evaluate_conflict_action(drp_ie, ext_beacon_slot, rsv, 0); +		if (uwb_rsv_is_owner(rsv)) { +			switch(action) { +			case UWB_DRP_CONFLICT_ACT2: +			case UWB_DRP_CONFLICT_ACT3: +				uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); +				rsv->needs_release_companion_mas = false; +				if (bow->can_reserve_extra_mases == false) +					uwb_rsv_backoff_win_increment(rc); +				uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); +			} +		} else { /* rsv is target */			 +			switch(action) { +			case UWB_DRP_CONFLICT_ACT2: +			case UWB_DRP_CONFLICT_ACT3: +				uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_EXPANDING_CONFLICT); +                                /* send_drp_avail_ie = true; */ +			} +		} +	} else { /* also base part of the reservation is conflicting */		 +		if (uwb_rsv_is_owner(rsv)) { +			uwb_rsv_backoff_win_increment(rc); +			/* remove companion part */ +			uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); + +			/* drop some mases with reason modified */ + +			/* put in the companion the mases to be dropped */ +			bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS); +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED); +		} else { /* it is a target rsv */ +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT); +                        /* send_drp_avail_ie = true; */ +		} +	} +} + +static void uwb_drp_handle_conflict_rsv(struct uwb_rc *rc, struct uwb_rsv *rsv, +					struct uwb_rc_evt_drp *drp_evt,  +					struct uwb_ie_drp *drp_ie, +					struct uwb_mas_bm *conflicting_mas) +{ +	struct uwb_rsv_move *mv; + +	/* check if the conflicting reservation has two drp_ies */ +	if (uwb_rsv_has_two_drp_ies(rsv)) { +		mv = &rsv->mv; +		if (bitmap_intersects(rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS)) { +			handle_conflict_expanding(drp_ie, drp_evt->beacon_slot_number, +						  rsv, false, conflicting_mas); +		} else { +			if (bitmap_intersects(mv->companion_mas.bm, conflicting_mas->bm, UWB_NUM_MAS)) { +				handle_conflict_expanding(drp_ie, drp_evt->beacon_slot_number, +							  rsv, true, conflicting_mas);	 +			} +		} +	} else if (bitmap_intersects(rsv->mas.bm, conflicting_mas->bm, UWB_NUM_MAS)) { +		handle_conflict_normal(drp_ie, drp_evt->beacon_slot_number, rsv, conflicting_mas); +	} +} + +static void uwb_drp_handle_all_conflict_rsv(struct uwb_rc *rc, +					    struct uwb_rc_evt_drp *drp_evt,  +					    struct uwb_ie_drp *drp_ie, +					    struct uwb_mas_bm *conflicting_mas) +{ +	struct uwb_rsv *rsv; +	 +	list_for_each_entry(rsv, &rc->reservations, rc_node) { +		uwb_drp_handle_conflict_rsv(rc, rsv, drp_evt, drp_ie, conflicting_mas);	 +	} +} +	  /*   * Based on the DRP IE, transition a target reservation to a new   * state.   */  static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv, -				   struct uwb_ie_drp *drp_ie) +				   struct uwb_ie_drp *drp_ie, struct uwb_rc_evt_drp *drp_evt)  {  	struct device *dev = &rc->uwb_dev.dev; +	struct uwb_rsv_move *mv = &rsv->mv;  	int status;  	enum uwb_drp_reason reason_code; - +	struct uwb_mas_bm mas; +	  	status = uwb_ie_drp_status(drp_ie);  	reason_code = uwb_ie_drp_reason_code(drp_ie); +	uwb_drp_ie_to_bm(&mas, drp_ie); -	if (status) { -		switch (reason_code) { -		case UWB_DRP_REASON_ACCEPTED: -			uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); -			break; -		case UWB_DRP_REASON_MODIFIED: -			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", -				reason_code, status); +	switch (reason_code) { +	case UWB_DRP_REASON_ACCEPTED: + +		if (rsv->state == UWB_RSV_STATE_T_CONFLICT) { +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_CONFLICT);  			break; -		default: -			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", -				 reason_code, status);  		} -	} else { -		switch (reason_code) { -		case UWB_DRP_REASON_ACCEPTED: -			/* New reservations are handled in uwb_rsv_find(). */ -			break; -		case UWB_DRP_REASON_DENIED: -			uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); -			break; -		case UWB_DRP_REASON_CONFLICT: -		case UWB_DRP_REASON_MODIFIED: -			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", -				reason_code, status); + +		if (rsv->state == UWB_RSV_STATE_T_EXPANDING_ACCEPTED) { +			/* drp_ie is companion */ +			if (!bitmap_equal(rsv->mas.bm, mas.bm, UWB_NUM_MAS)) +				/* stroke companion */ +				uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_EXPANDING_ACCEPTED);	 +		} else { +			if (!bitmap_equal(rsv->mas.bm, mas.bm, UWB_NUM_MAS)) { +				if (uwb_drp_avail_reserve_pending(rc, &mas) == -EBUSY) { +					/* FIXME: there is a conflict, find +					 * the conflicting reservations and +					 * take a sensible action. Consider +					 * that in drp_ie there is the +					 * "neighbour" */ +					uwb_drp_handle_all_conflict_rsv(rc, drp_evt, drp_ie, &mas); +				} else { +					/* accept the extra reservation */ +					bitmap_copy(mv->companion_mas.bm, mas.bm, UWB_NUM_MAS); +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_EXPANDING_ACCEPTED); +				} +			} else { +				if (status) { +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); +				} +			} +			 +		} +		break; + +	case UWB_DRP_REASON_MODIFIED: +		/* check to see if we have already modified the reservation */ +		if (bitmap_equal(rsv->mas.bm, mas.bm, UWB_NUM_MAS)) { +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED);  			break; -		default: -			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", -				 reason_code, status);  		} + +		/* find if the owner wants to expand or reduce */ +		if (bitmap_subset(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) { +			/* owner is reducing */ +			bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, mas.bm, UWB_NUM_MAS); +			uwb_drp_avail_release(rsv->rc, &mv->companion_mas); +		} + +		bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS); +		uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_RESIZED); +		break; +	default: +		dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", +			 reason_code, status);  	}  } @@ -179,23 +450,60 @@ static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv,   * state.   */  static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv, -				  struct uwb_ie_drp *drp_ie) +				  struct uwb_dev *src, struct uwb_ie_drp *drp_ie, +				  struct uwb_rc_evt_drp *drp_evt)  {  	struct device *dev = &rc->uwb_dev.dev; +	struct uwb_rsv_move *mv = &rsv->mv;  	int status;  	enum uwb_drp_reason reason_code; +	struct uwb_mas_bm mas;  	status = uwb_ie_drp_status(drp_ie);  	reason_code = uwb_ie_drp_reason_code(drp_ie); +	uwb_drp_ie_to_bm(&mas, drp_ie);  	if (status) {  		switch (reason_code) {  		case UWB_DRP_REASON_ACCEPTED: -			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); -			break; -		case UWB_DRP_REASON_MODIFIED: -			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", -				reason_code, status); +			switch (rsv->state) { +			case UWB_RSV_STATE_O_PENDING: +			case UWB_RSV_STATE_O_INITIATED: +			case UWB_RSV_STATE_O_ESTABLISHED: +				uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); +				break; +			case UWB_RSV_STATE_O_MODIFIED: +				if (bitmap_equal(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) { +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); +				} else { +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MODIFIED);	 +				} +				break; +				 +			case UWB_RSV_STATE_O_MOVE_REDUCING: /* shouldn' t be a problem */ +				if (bitmap_equal(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) { +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); +				} else { +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING);	 +				} +				break; +			case UWB_RSV_STATE_O_MOVE_EXPANDING: +				if (bitmap_equal(mas.bm, mv->companion_mas.bm, UWB_NUM_MAS)) { +					/* Companion reservation accepted */ +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); +				} else { +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); +				} +				break; +			case UWB_RSV_STATE_O_MOVE_COMBINING: +				if (bitmap_equal(mas.bm, rsv->mas.bm, UWB_NUM_MAS)) +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); +				else +					uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); +				break; +			default: +				break;	 +			}  			break;  		default:  			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", @@ -210,9 +518,10 @@ static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv,  			uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);  			break;  		case UWB_DRP_REASON_CONFLICT: -		case UWB_DRP_REASON_MODIFIED: -			dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n", -				reason_code, status); +			/* resolve the conflict */ +			bitmap_complement(mas.bm, src->last_availability_bm, +					  UWB_NUM_MAS); +			uwb_drp_handle_conflict_rsv(rc, rsv, drp_evt, drp_ie, &mas);  			break;  		default:  			dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n", @@ -221,12 +530,110 @@ static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv,  	}  } +static void uwb_cnflt_alien_stroke_timer(struct uwb_cnflt_alien *cnflt) +{ +	unsigned timeout_us = UWB_MAX_LOST_BEACONS * UWB_SUPERFRAME_LENGTH_US; +	mod_timer(&cnflt->timer, jiffies + usecs_to_jiffies(timeout_us)); +} + +static void uwb_cnflt_update_work(struct work_struct *work) +{ +	struct uwb_cnflt_alien *cnflt = container_of(work, +						     struct uwb_cnflt_alien, +						     cnflt_update_work); +	struct uwb_cnflt_alien *c; +	struct uwb_rc *rc = cnflt->rc; +	 +	unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; +	 +	mutex_lock(&rc->rsvs_mutex); + +	list_del(&cnflt->rc_node); + +	/* update rc global conflicting alien bitmap */ +	bitmap_zero(rc->cnflt_alien_bitmap.bm, UWB_NUM_MAS); + +	list_for_each_entry(c, &rc->cnflt_alien_list, rc_node) { +		bitmap_or(rc->cnflt_alien_bitmap.bm, rc->cnflt_alien_bitmap.bm, c->mas.bm, UWB_NUM_MAS);			 +	} +	 +	queue_delayed_work(rc->rsv_workq, &rc->rsv_alien_bp_work, usecs_to_jiffies(delay_us)); + +	kfree(cnflt); +	mutex_unlock(&rc->rsvs_mutex); +} + +static void uwb_cnflt_timer(unsigned long arg) +{ +	struct uwb_cnflt_alien *cnflt = (struct uwb_cnflt_alien *)arg; + +	queue_work(cnflt->rc->rsv_workq, &cnflt->cnflt_update_work); +} +  /* - * Process a received DRP IE, it's either for a reservation owned by - * the RC or targeted at it (or it's for a WUSB cluster reservation). + * We have received an DRP_IE of type Alien BP and we need to make + * sure we do not transmit in conflicting MASs.   */ -static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src, -		     struct uwb_ie_drp *drp_ie) +static void uwb_drp_handle_alien_drp(struct uwb_rc *rc, struct uwb_ie_drp *drp_ie) +{ +	struct device *dev = &rc->uwb_dev.dev; +	struct uwb_mas_bm mas; +	struct uwb_cnflt_alien *cnflt; +	char buf[72]; +	unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; +	 +	uwb_drp_ie_to_bm(&mas, drp_ie); +	bitmap_scnprintf(buf, sizeof(buf), mas.bm, UWB_NUM_MAS); +	 +	list_for_each_entry(cnflt, &rc->cnflt_alien_list, rc_node) { +		if (bitmap_equal(cnflt->mas.bm, mas.bm, UWB_NUM_MAS)) { +			/* Existing alien BP reservation conflicting +			 * bitmap, just reset the timer */ +			uwb_cnflt_alien_stroke_timer(cnflt); +			return; +		} +	} + +	/* New alien BP reservation conflicting bitmap */ + +	/* alloc and initialize new uwb_cnflt_alien */ +	cnflt = kzalloc(sizeof(struct uwb_cnflt_alien), GFP_KERNEL); +	if (!cnflt) +		dev_err(dev, "failed to alloc uwb_cnflt_alien struct\n"); +	INIT_LIST_HEAD(&cnflt->rc_node); +	init_timer(&cnflt->timer); +	cnflt->timer.function = uwb_cnflt_timer; +	cnflt->timer.data     = (unsigned long)cnflt; + +	cnflt->rc = rc; +	INIT_WORK(&cnflt->cnflt_update_work, uwb_cnflt_update_work); +	 +	bitmap_copy(cnflt->mas.bm, mas.bm, UWB_NUM_MAS); + +	list_add_tail(&cnflt->rc_node, &rc->cnflt_alien_list); + +	/* update rc global conflicting alien bitmap */ +	bitmap_or(rc->cnflt_alien_bitmap.bm, rc->cnflt_alien_bitmap.bm, mas.bm, UWB_NUM_MAS); + +	queue_delayed_work(rc->rsv_workq, &rc->rsv_alien_bp_work, usecs_to_jiffies(delay_us)); +	 +	/* start the timer */ +	uwb_cnflt_alien_stroke_timer(cnflt); +} + +static void uwb_drp_process_not_involved(struct uwb_rc *rc, +					 struct uwb_rc_evt_drp *drp_evt,  +					 struct uwb_ie_drp *drp_ie) +{ +	struct uwb_mas_bm mas; +	 +	uwb_drp_ie_to_bm(&mas, drp_ie); +	uwb_drp_handle_all_conflict_rsv(rc, drp_evt, drp_ie, &mas); +} + +static void uwb_drp_process_involved(struct uwb_rc *rc, struct uwb_dev *src, +				     struct uwb_rc_evt_drp *drp_evt, +				     struct uwb_ie_drp *drp_ie)  {  	struct uwb_rsv *rsv; @@ -239,7 +646,7 @@ static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src,  		 */  		return;  	} - +	  	/*  	 * Do nothing with DRP IEs for reservations that have been  	 * terminated. @@ -248,13 +655,43 @@ static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src,  		uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);  		return;  	} - +			  	if (uwb_ie_drp_owner(drp_ie)) -		uwb_drp_process_target(rc, rsv, drp_ie); +		uwb_drp_process_target(rc, rsv, drp_ie, drp_evt); +	else +		uwb_drp_process_owner(rc, rsv, src, drp_ie, drp_evt); +	 +} + + +static bool uwb_drp_involves_us(struct uwb_rc *rc, struct uwb_ie_drp *drp_ie) +{ +	return uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, &drp_ie->dev_addr) == 0; +} + +/* + * Process a received DRP IE. + */ +static void uwb_drp_process(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, +			    struct uwb_dev *src, struct uwb_ie_drp *drp_ie) +{ +	if (uwb_ie_drp_type(drp_ie) == UWB_DRP_TYPE_ALIEN_BP) +		uwb_drp_handle_alien_drp(rc, drp_ie); +	else if (uwb_drp_involves_us(rc, drp_ie)) +		uwb_drp_process_involved(rc, src, drp_evt, drp_ie);  	else -		uwb_drp_process_owner(rc, rsv, drp_ie); +		uwb_drp_process_not_involved(rc, drp_evt, drp_ie);  } +/* + * Process a received DRP Availability IE + */ +static void uwb_drp_availability_process(struct uwb_rc *rc, struct uwb_dev *src, +					 struct uwb_ie_drp_avail *drp_availability_ie) +{ +	bitmap_copy(src->last_availability_bm, +		    drp_availability_ie->bmp, UWB_NUM_MAS); +}  /*   * Process all the DRP IEs (both DRP IEs and the DRP Availability IE) @@ -276,10 +713,10 @@ void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,  		switch (ie_hdr->element_id) {  		case UWB_IE_DRP_AVAILABILITY: -			/* FIXME: does something need to be done with this? */ +			uwb_drp_availability_process(rc, src_dev, (struct uwb_ie_drp_avail *)ie_hdr);  			break;  		case UWB_IE_DRP: -			uwb_drp_process(rc, src_dev, (struct uwb_ie_drp *)ie_hdr); +			uwb_drp_process(rc, drp_evt, src_dev, (struct uwb_ie_drp *)ie_hdr);  			break;  		default:  			dev_warn(dev, "unexpected IE in DRP notification\n"); @@ -292,55 +729,6 @@ void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,  			 (int)ielen);  } - -/* - * Go through all the DRP IEs and find the ones that conflict with our - * reservations. - * - * FIXME: must resolve the conflict according the the rules in - * [ECMA-368]. - */ -static -void uwb_drp_process_conflict_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt, -				  size_t ielen, struct uwb_dev *src_dev) -{ -	struct device *dev = &rc->uwb_dev.dev; -	struct uwb_ie_hdr *ie_hdr; -	struct uwb_ie_drp *drp_ie; -	void *ptr; - -	ptr = drp_evt->ie_data; -	for (;;) { -		ie_hdr = uwb_ie_next(&ptr, &ielen); -		if (!ie_hdr) -			break; - -		drp_ie = container_of(ie_hdr, struct uwb_ie_drp, hdr); - -		/* FIXME: check if this DRP IE conflicts. */ -	} - -	if (ielen > 0) -		dev_warn(dev, "%d octets remaining in DRP notification\n", -			 (int)ielen); -} - - -/* - * Terminate all reservations owned by, or targeted at, 'uwb_dev'. - */ -static void uwb_drp_terminate_all(struct uwb_rc *rc, struct uwb_dev *uwb_dev) -{ -	struct uwb_rsv *rsv; - -	list_for_each_entry(rsv, &rc->reservations, rc_node) { -		if (rsv->owner == uwb_dev -		    || (rsv->target.type == UWB_RSV_TARGET_DEV && rsv->target.dev == uwb_dev)) -			uwb_rsv_remove(rsv); -	} -} - -  /**   * uwbd_evt_handle_rc_drp - handle a DRP_IE event   * @evt: the DRP_IE event from the radio controller @@ -381,7 +769,6 @@ int uwbd_evt_handle_rc_drp(struct uwb_event *evt)  	size_t ielength, bytes_left;  	struct uwb_dev_addr src_addr;  	struct uwb_dev *src_dev; -	int reason;  	/* Is there enough data to decode the event (and any IEs in  	   its payload)? */ @@ -417,22 +804,8 @@ int uwbd_evt_handle_rc_drp(struct uwb_event *evt)  	mutex_lock(&rc->rsvs_mutex); -	reason = uwb_rc_evt_drp_reason(drp_evt); - -	switch (reason) { -	case UWB_DRP_NOTIF_DRP_IE_RCVD: -		uwb_drp_process_all(rc, drp_evt, ielength, src_dev); -		break; -	case UWB_DRP_NOTIF_CONFLICT: -		uwb_drp_process_conflict_all(rc, drp_evt, ielength, src_dev); -		break; -	case UWB_DRP_NOTIF_TERMINATE: -		uwb_drp_terminate_all(rc, src_dev); -		break; -	default: -		dev_warn(dev, "ignored DRP event with reason code: %d\n", reason); -		break; -	} +	/* We do not distinguish from the reason */ +	uwb_drp_process_all(rc, drp_evt, ielength, src_dev);  	mutex_unlock(&rc->rsvs_mutex); diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c index 1cd84f92754..165aec6a8f9 100644 --- a/drivers/uwb/rsv.c +++ b/drivers/uwb/rsv.c @@ -17,20 +17,31 @@   */  #include <linux/kernel.h>  #include <linux/uwb.h> +#include <linux/random.h>  #include "uwb-internal.h"  static void uwb_rsv_timer(unsigned long arg);  static const char *rsv_states[] = { -	[UWB_RSV_STATE_NONE]          = "none", -	[UWB_RSV_STATE_O_INITIATED]   = "initiated", -	[UWB_RSV_STATE_O_PENDING]     = "pending", -	[UWB_RSV_STATE_O_MODIFIED]    = "modified", -	[UWB_RSV_STATE_O_ESTABLISHED] = "established", -	[UWB_RSV_STATE_T_ACCEPTED]    = "accepted", -	[UWB_RSV_STATE_T_DENIED]      = "denied", -	[UWB_RSV_STATE_T_PENDING]     = "pending", +	[UWB_RSV_STATE_NONE]                 = "none            ", +	[UWB_RSV_STATE_O_INITIATED]          = "o initiated     ", +	[UWB_RSV_STATE_O_PENDING]            = "o pending       ", +	[UWB_RSV_STATE_O_MODIFIED]           = "o modified      ", +	[UWB_RSV_STATE_O_ESTABLISHED]        = "o established   ", +	[UWB_RSV_STATE_O_TO_BE_MOVED]        = "o to be moved   ", +	[UWB_RSV_STATE_O_MOVE_EXPANDING]     = "o move expanding", +	[UWB_RSV_STATE_O_MOVE_COMBINING]     = "o move combining", +	[UWB_RSV_STATE_O_MOVE_REDUCING]      = "o move reducing ", +	[UWB_RSV_STATE_T_ACCEPTED]           = "t accepted      ", +	[UWB_RSV_STATE_T_CONFLICT]           = "t conflict      ", +	[UWB_RSV_STATE_T_PENDING]            = "t pending       ", +	[UWB_RSV_STATE_T_DENIED]             = "t denied        ", +	[UWB_RSV_STATE_T_RESIZED]            = "t resized       ", +	[UWB_RSV_STATE_T_EXPANDING_ACCEPTED] = "t expanding acc ", +	[UWB_RSV_STATE_T_EXPANDING_CONFLICT] = "t expanding conf", +	[UWB_RSV_STATE_T_EXPANDING_PENDING]  = "t expanding pend", +	[UWB_RSV_STATE_T_EXPANDING_DENIED]   = "t expanding den ",  };  static const char *rsv_types[] = { @@ -41,6 +52,31 @@ static const char *rsv_types[] = {  	[UWB_DRP_TYPE_PCA]      = "pca",  }; +bool uwb_rsv_has_two_drp_ies(struct uwb_rsv *rsv) +{ +	static const bool has_two_drp_ies[] = { +		[UWB_RSV_STATE_O_INITIATED]               = false, +		[UWB_RSV_STATE_O_PENDING]                 = false, +		[UWB_RSV_STATE_O_MODIFIED]                = false, +		[UWB_RSV_STATE_O_ESTABLISHED]             = false, +		[UWB_RSV_STATE_O_TO_BE_MOVED]             = false, +		[UWB_RSV_STATE_O_MOVE_COMBINING]          = false, +		[UWB_RSV_STATE_O_MOVE_REDUCING]           = false, +		[UWB_RSV_STATE_O_MOVE_EXPANDING]          = true, +		[UWB_RSV_STATE_T_ACCEPTED]                = false, +		[UWB_RSV_STATE_T_CONFLICT]                = false, +		[UWB_RSV_STATE_T_PENDING]                 = false, +		[UWB_RSV_STATE_T_DENIED]                  = false, +		[UWB_RSV_STATE_T_RESIZED]                 = false, +		[UWB_RSV_STATE_T_EXPANDING_ACCEPTED]      = true, +		[UWB_RSV_STATE_T_EXPANDING_CONFLICT]      = true, +		[UWB_RSV_STATE_T_EXPANDING_PENDING]       = true, +		[UWB_RSV_STATE_T_EXPANDING_DENIED]        = true, +	}; + +	return has_two_drp_ies[rsv->state]; +} +  /**   * uwb_rsv_state_str - return a string for a reservation state   * @state: the reservation state. @@ -65,7 +101,7 @@ const char *uwb_rsv_type_str(enum uwb_drp_type type)  }  EXPORT_SYMBOL_GPL(uwb_rsv_type_str); -static void uwb_rsv_dump(struct uwb_rsv *rsv) +void uwb_rsv_dump(char *text, struct uwb_rsv *rsv)  {  	struct device *dev = &rsv->rc->uwb_dev.dev;  	struct uwb_dev_addr devaddr; @@ -88,12 +124,12 @@ static void uwb_rsv_release(struct kref *kref)  	kfree(rsv);  } -static void uwb_rsv_get(struct uwb_rsv *rsv) +void uwb_rsv_get(struct uwb_rsv *rsv)  {  	kref_get(&rsv->kref);  } -static void uwb_rsv_put(struct uwb_rsv *rsv) +void uwb_rsv_put(struct uwb_rsv *rsv)  {  	kref_put(&rsv->kref, uwb_rsv_release);  } @@ -108,6 +144,7 @@ static void uwb_rsv_put(struct uwb_rsv *rsv)  static int uwb_rsv_get_stream(struct uwb_rsv *rsv)  {  	struct uwb_rc *rc = rsv->rc; +	struct device *dev = &rc->uwb_dev.dev;  	unsigned long *streams_bm;  	int stream; @@ -129,12 +166,15 @@ static int uwb_rsv_get_stream(struct uwb_rsv *rsv)  	rsv->stream = stream;  	set_bit(stream, streams_bm); +	dev_dbg(dev, "get stream %d\n", rsv->stream); +  	return 0;  }  static void uwb_rsv_put_stream(struct uwb_rsv *rsv)  {  	struct uwb_rc *rc = rsv->rc; +	struct device *dev = &rc->uwb_dev.dev;  	unsigned long *streams_bm;  	switch (rsv->target.type) { @@ -149,86 +189,52 @@ static void uwb_rsv_put_stream(struct uwb_rsv *rsv)  	}  	clear_bit(rsv->stream, streams_bm); + +	dev_dbg(dev, "put stream %d\n", rsv->stream);  } -/* - * Generate a MAS allocation with a single row component. - */ -static void uwb_rsv_gen_alloc_row(struct uwb_mas_bm *mas, -				  int first_mas, int mas_per_zone, -				  int zs, int ze) +void uwb_rsv_backoff_win_timer(unsigned long arg)  { -	struct uwb_mas_bm col; -	int z; +	struct uwb_drp_backoff_win *bow = (struct uwb_drp_backoff_win *)arg; +	struct uwb_rc *rc = container_of(bow, struct uwb_rc, bow); +	struct device *dev = &rc->uwb_dev.dev; -	bitmap_zero(mas->bm, UWB_NUM_MAS); -	bitmap_zero(col.bm, UWB_NUM_MAS); -	bitmap_fill(col.bm, mas_per_zone); -	bitmap_shift_left(col.bm, col.bm, first_mas + zs * UWB_MAS_PER_ZONE, UWB_NUM_MAS); - -	for (z = zs; z <= ze; z++) { -		bitmap_or(mas->bm, mas->bm, col.bm, UWB_NUM_MAS); -		bitmap_shift_left(col.bm, col.bm, UWB_MAS_PER_ZONE, UWB_NUM_MAS); +	bow->can_reserve_extra_mases = true; +	if (bow->total_expired <= 4) { +		bow->total_expired++; +	} else { +		/* after 4 backoff window has expired we can exit from +		 * the backoff procedure */ +		bow->total_expired = 0; +		bow->window = UWB_DRP_BACKOFF_WIN_MIN >> 1;  	} +	dev_dbg(dev, "backoff_win_timer total_expired=%d, n=%d\n: ", bow->total_expired, bow->n); + +	/* try to relocate all the "to be moved" relocations */ +	uwb_rsv_handle_drp_avail_change(rc);  } -/* - * Allocate some MAS for this reservation based on current local - * availability, the reservation parameters (max_mas, min_mas, - * sparsity), and the WiMedia rules for MAS allocations. - * - * Returns -EBUSY is insufficient free MAS are available. - * - * FIXME: to simplify this, only safe reservations with a single row - * component in zones 1 to 15 are tried (zone 0 is skipped to avoid - * problems with the MAS reserved for the BP). - * - * [ECMA-368] section B.2. - */ -static int uwb_rsv_alloc_mas(struct uwb_rsv *rsv) +void uwb_rsv_backoff_win_increment(struct uwb_rc *rc)  { -	static const int safe_mas_in_row[UWB_NUM_ZONES] = { -		8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, -	}; -	int n, r; -	struct uwb_mas_bm mas; -	bool found = false; +	struct uwb_drp_backoff_win *bow = &rc->bow; +	struct device *dev = &rc->uwb_dev.dev; +	unsigned timeout_us; -	/* -	 * Search all valid safe allocations until either: too few MAS -	 * are available; or the smallest allocation with sufficient -	 * MAS is found. -	 * -	 * The top of the zones are preferred, so space for larger -	 * allocations is available in the bottom of the zone (e.g., a -	 * 15 MAS allocation should start in row 14 leaving space for -	 * a 120 MAS allocation at row 0). -	 */ -	for (n = safe_mas_in_row[0]; n >= 1; n--) { -		int num_mas; +	dev_dbg(dev, "backoff_win_increment: window=%d\n", bow->window); -		num_mas = n * (UWB_NUM_ZONES - 1); -		if (num_mas < rsv->min_mas) -			break; -		if (found && num_mas < rsv->max_mas) -			break; +	bow->can_reserve_extra_mases = false; -		for (r = UWB_MAS_PER_ZONE-1;  r >= 0; r--) { -			if (safe_mas_in_row[r] < n) -				continue; -			uwb_rsv_gen_alloc_row(&mas, r, n, 1, UWB_NUM_ZONES); -			if (uwb_drp_avail_reserve_pending(rsv->rc, &mas) == 0) { -				found = true; -				break; -			} -		} -	} +	if((bow->window << 1) == UWB_DRP_BACKOFF_WIN_MAX) +		return; -	if (!found) -		return -EBUSY; +	bow->window <<= 1; +	bow->n = random32() & (bow->window - 1); +	dev_dbg(dev, "new_window=%d, n=%d\n: ", bow->window, bow->n); -	bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS); -	return 0; +	/* reset the timer associated variables */ +	timeout_us = bow->n * UWB_SUPERFRAME_LENGTH_US; +	bow->total_expired = 0; +	mod_timer(&bow->timer, jiffies + usecs_to_jiffies(timeout_us));		  }  static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv) @@ -241,13 +247,16 @@ static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv)  	 * received.  	 */  	if (rsv->is_multicast) { -		if (rsv->state == UWB_RSV_STATE_O_INITIATED) +		if (rsv->state == UWB_RSV_STATE_O_INITIATED +		    || rsv->state == UWB_RSV_STATE_O_MOVE_EXPANDING +		    || rsv->state == UWB_RSV_STATE_O_MOVE_COMBINING +		    || rsv->state == UWB_RSV_STATE_O_MOVE_REDUCING)  			sframes = 1;  		if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED)  			sframes = 0; +		  	} -	rsv->expired = false;  	if (sframes > 0) {  		/*  		 * Add an additional 2 superframes to account for the @@ -269,7 +278,7 @@ static void uwb_rsv_state_update(struct uwb_rsv *rsv,  	rsv->state = new_state;  	rsv->ie_valid = false; -	uwb_rsv_dump(rsv); +	uwb_rsv_dump("SU", rsv);  	uwb_rsv_stroke_timer(rsv);  	uwb_rsv_sched_update(rsv->rc); @@ -283,10 +292,17 @@ static void uwb_rsv_callback(struct uwb_rsv *rsv)  void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state)  { +	struct uwb_rsv_move *mv = &rsv->mv; +  	if (rsv->state == new_state) {  		switch (rsv->state) {  		case UWB_RSV_STATE_O_ESTABLISHED: +		case UWB_RSV_STATE_O_MOVE_EXPANDING: +		case UWB_RSV_STATE_O_MOVE_COMBINING: +		case UWB_RSV_STATE_O_MOVE_REDUCING:  		case UWB_RSV_STATE_T_ACCEPTED: +		case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: +		case UWB_RSV_STATE_T_RESIZED:  		case UWB_RSV_STATE_NONE:  			uwb_rsv_stroke_timer(rsv);  			break; @@ -298,11 +314,10 @@ void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state)  		return;  	} +	uwb_rsv_dump("SC", rsv); +  	switch (new_state) {  	case UWB_RSV_STATE_NONE: -		uwb_drp_avail_release(rsv->rc, &rsv->mas); -		if (uwb_rsv_is_owner(rsv)) -			uwb_rsv_put_stream(rsv);  		uwb_rsv_state_update(rsv, UWB_RSV_STATE_NONE);  		uwb_rsv_callback(rsv);  		break; @@ -312,12 +327,45 @@ void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state)  	case UWB_RSV_STATE_O_PENDING:  		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_PENDING);  		break; +	case UWB_RSV_STATE_O_MODIFIED: +		/* in the companion there are the MASes to drop */ +		bitmap_andnot(rsv->mas.bm, rsv->mas.bm, mv->companion_mas.bm, UWB_NUM_MAS); +		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MODIFIED); +		break;  	case UWB_RSV_STATE_O_ESTABLISHED: +		if (rsv->state == UWB_RSV_STATE_O_MODIFIED +		    || rsv->state == UWB_RSV_STATE_O_MOVE_REDUCING) { +			uwb_drp_avail_release(rsv->rc, &mv->companion_mas); +			rsv->needs_release_companion_mas = false; +		}  		uwb_drp_avail_reserve(rsv->rc, &rsv->mas);  		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_ESTABLISHED);  		uwb_rsv_callback(rsv);  		break; +	case UWB_RSV_STATE_O_MOVE_EXPANDING: +		rsv->needs_release_companion_mas = true; +		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); +		break; +	case UWB_RSV_STATE_O_MOVE_COMBINING: +		rsv->needs_release_companion_mas = false; +		uwb_drp_avail_reserve(rsv->rc, &mv->companion_mas); +		bitmap_or(rsv->mas.bm, rsv->mas.bm, mv->companion_mas.bm, UWB_NUM_MAS); +		rsv->mas.safe   += mv->companion_mas.safe; +		rsv->mas.unsafe += mv->companion_mas.unsafe; +		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); +		break; +	case UWB_RSV_STATE_O_MOVE_REDUCING: +		bitmap_andnot(mv->companion_mas.bm, rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS); +		rsv->needs_release_companion_mas = true; +		rsv->mas.safe   = mv->final_mas.safe; +		rsv->mas.unsafe = mv->final_mas.unsafe; +		bitmap_copy(rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS); +		bitmap_copy(rsv->mas.unsafe_bm, mv->final_mas.unsafe_bm, UWB_NUM_MAS); +		uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); +		break;  	case UWB_RSV_STATE_T_ACCEPTED: +	case UWB_RSV_STATE_T_RESIZED: +		rsv->needs_release_companion_mas = false;  		uwb_drp_avail_reserve(rsv->rc, &rsv->mas);  		uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_ACCEPTED);  		uwb_rsv_callback(rsv); @@ -325,12 +373,82 @@ void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state)  	case UWB_RSV_STATE_T_DENIED:  		uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_DENIED);  		break; +	case UWB_RSV_STATE_T_CONFLICT: +		uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_CONFLICT); +		break; +	case UWB_RSV_STATE_T_PENDING: +		uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_PENDING); +		break; +	case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: +		rsv->needs_release_companion_mas = true; +		uwb_drp_avail_reserve(rsv->rc, &mv->companion_mas); +		uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_EXPANDING_ACCEPTED); +		break;  	default:  		dev_err(&rsv->rc->uwb_dev.dev, "unhandled state: %s (%d)\n",  			uwb_rsv_state_str(new_state), new_state);  	}  } +static void uwb_rsv_handle_timeout_work(struct work_struct *work) +{ +	struct uwb_rsv *rsv = container_of(work, struct uwb_rsv, +					   handle_timeout_work); +	struct uwb_rc *rc = rsv->rc; + +	mutex_lock(&rc->rsvs_mutex); + +	uwb_rsv_dump("TO", rsv); + +	switch (rsv->state) { +	case UWB_RSV_STATE_O_INITIATED: +		if (rsv->is_multicast) { +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); +			goto unlock; +		} +		break; +	case UWB_RSV_STATE_O_MOVE_EXPANDING: +		if (rsv->is_multicast) { +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_COMBINING); +			goto unlock; +		} +		break; +	case UWB_RSV_STATE_O_MOVE_COMBINING: +		if (rsv->is_multicast) { +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_REDUCING); +			goto unlock; +		} +		break; +	case UWB_RSV_STATE_O_MOVE_REDUCING: +		if (rsv->is_multicast) { +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED); +			goto unlock; +		} +		break; +	case UWB_RSV_STATE_O_ESTABLISHED: +		if (rsv->is_multicast) +			goto unlock; +		break; +	case UWB_RSV_STATE_T_EXPANDING_ACCEPTED: +		/* +		 * The time out could be for the main or of the +		 * companion DRP, assume it's for the companion and +		 * drop that first.  A further time out is required to +		 * drop the main. +		 */ +		uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED); +		uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); +		goto unlock; +	default: +		break; +	} + +	uwb_rsv_remove(rsv); + +unlock: +	mutex_unlock(&rc->rsvs_mutex); +} +  static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc)  {  	struct uwb_rsv *rsv; @@ -347,6 +465,7 @@ static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc)  	rsv->timer.data     = (unsigned long)rsv;  	rsv->rc = rc; +	INIT_WORK(&rsv->handle_timeout_work, uwb_rsv_handle_timeout_work);  	return rsv;  } @@ -381,8 +500,18 @@ EXPORT_SYMBOL_GPL(uwb_rsv_create);  void uwb_rsv_remove(struct uwb_rsv *rsv)  { +	uwb_rsv_dump("RM", rsv); +  	if (rsv->state != UWB_RSV_STATE_NONE)  		uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE); + +	if (rsv->needs_release_companion_mas) +		uwb_drp_avail_release(rsv->rc, &rsv->mv.companion_mas); +	uwb_drp_avail_release(rsv->rc, &rsv->mas); + +	if (uwb_rsv_is_owner(rsv)) +		uwb_rsv_put_stream(rsv); +	  	del_timer_sync(&rsv->timer);  	uwb_dev_put(rsv->owner);  	if (rsv->target.type == UWB_RSV_TARGET_DEV) @@ -409,7 +538,7 @@ EXPORT_SYMBOL_GPL(uwb_rsv_destroy);   * @rsv: the reservation   *   * The PAL should fill in @rsv's owner, target, type, max_mas, - * min_mas, sparsity and is_multicast fields.  If the target is a + * min_mas, max_interval and is_multicast fields.  If the target is a   * uwb_dev it must be referenced.   *   * The reservation's callback will be called when the reservation is @@ -418,16 +547,27 @@ EXPORT_SYMBOL_GPL(uwb_rsv_destroy);  int uwb_rsv_establish(struct uwb_rsv *rsv)  {  	struct uwb_rc *rc = rsv->rc; +	struct uwb_mas_bm available;  	int ret;  	mutex_lock(&rc->rsvs_mutex); -  	ret = uwb_rsv_get_stream(rsv);  	if (ret)  		goto out; -	ret = uwb_rsv_alloc_mas(rsv); -	if (ret) { +	rsv->tiebreaker = random32() & 1; +	/* get available mas bitmap */ +	uwb_drp_available(rc, &available); + +	ret = uwb_rsv_find_best_allocation(rsv, &available, &rsv->mas); +	if (ret == UWB_RSV_ALLOC_NOT_FOUND) { +		ret = -EBUSY; +		uwb_rsv_put_stream(rsv); +		goto out; +	} + +	ret = uwb_drp_avail_reserve_pending(rc, &rsv->mas); +	if (ret != 0) {  		uwb_rsv_put_stream(rsv);  		goto out;  	} @@ -448,16 +588,71 @@ EXPORT_SYMBOL_GPL(uwb_rsv_establish);   * @rsv: the reservation to modify   * @max_mas: new maximum MAS to reserve   * @min_mas: new minimum MAS to reserve - * @sparsity: new sparsity to use + * @max_interval: new max_interval to use   *   * FIXME: implement this once there are PALs that use it.   */ -int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int sparsity) +int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int max_interval)  {  	return -ENOSYS;  }  EXPORT_SYMBOL_GPL(uwb_rsv_modify); +/* + * move an already established reservation (rc->rsvs_mutex must to be + * taken when tis function is called) + */ +int uwb_rsv_try_move(struct uwb_rsv *rsv, struct uwb_mas_bm *available) +{ +	struct uwb_rc *rc = rsv->rc; +	struct uwb_drp_backoff_win *bow = &rc->bow; +	struct device *dev = &rc->uwb_dev.dev; +	struct uwb_rsv_move *mv; +	int ret = 0; +  +	if (bow->can_reserve_extra_mases == false) +		return -EBUSY; + +	mv = &rsv->mv; + +	if (uwb_rsv_find_best_allocation(rsv, available, &mv->final_mas) == UWB_RSV_ALLOC_FOUND) { + +		if (!bitmap_equal(rsv->mas.bm, mv->final_mas.bm, UWB_NUM_MAS)) { +			/* We want to move the reservation */ +			bitmap_andnot(mv->companion_mas.bm, mv->final_mas.bm, rsv->mas.bm, UWB_NUM_MAS); +			uwb_drp_avail_reserve_pending(rc, &mv->companion_mas); +			uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_MOVE_EXPANDING); +		} +	} else { +		dev_dbg(dev, "new allocation not found\n"); +	} +	 +	return ret; +} + +/* It will try to move every reservation in state O_ESTABLISHED giving + * to the MAS allocator algorithm an availability that is the real one + * plus the allocation already established from the reservation. */ +void uwb_rsv_handle_drp_avail_change(struct uwb_rc *rc) +{ +	struct uwb_drp_backoff_win *bow = &rc->bow; +	struct uwb_rsv *rsv; +	struct uwb_mas_bm mas; +	 +	if (bow->can_reserve_extra_mases == false) +		return; + +	list_for_each_entry(rsv, &rc->reservations, rc_node) { +		if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED || +		    rsv->state == UWB_RSV_STATE_O_TO_BE_MOVED) { +			uwb_drp_available(rc, &mas); +			bitmap_or(mas.bm, mas.bm, rsv->mas.bm, UWB_NUM_MAS); +			uwb_rsv_try_move(rsv, &mas); +		} +	} +	 +} +  /**   * uwb_rsv_terminate - terminate an established reservation   * @rsv: the reservation to terminate @@ -546,6 +741,7 @@ static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc,  	uwb_dev_get(rsv->owner);  	rsv->target.type = UWB_RSV_TARGET_DEV;  	rsv->target.dev  = &rc->uwb_dev; +	uwb_dev_get(&rc->uwb_dev);  	rsv->type        = uwb_ie_drp_type(drp_ie);  	rsv->stream      = uwb_ie_drp_stream_index(drp_ie);  	uwb_drp_ie_to_bm(&rsv->mas, drp_ie); @@ -567,12 +763,34 @@ static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc,  	list_add_tail(&rsv->rc_node, &rc->reservations);  	state = rsv->state;  	rsv->state = UWB_RSV_STATE_NONE; -	uwb_rsv_set_state(rsv, state); + +	/* FIXME: do something sensible here */ +	if (state == UWB_RSV_STATE_T_ACCEPTED +	    && uwb_drp_avail_reserve_pending(rc, &rsv->mas) == -EBUSY) { +		/* FIXME: do something sensible here */ +	} else { +		uwb_rsv_set_state(rsv, state); +	}  	return rsv;  }  /** + * uwb_rsv_get_usable_mas - get the bitmap of the usable MAS of a reservations + * @rsv: the reservation. + * @mas: returns the available MAS. + * + * The usable MAS of a reservation may be less than the negotiated MAS + * if alien BPs are present. + */ +void uwb_rsv_get_usable_mas(struct uwb_rsv *rsv, struct uwb_mas_bm *mas) +{ +	bitmap_zero(mas->bm, UWB_NUM_MAS); +	bitmap_andnot(mas->bm, rsv->mas.bm, rsv->rc->cnflt_alien_bitmap.bm, UWB_NUM_MAS); +} +EXPORT_SYMBOL_GPL(uwb_rsv_get_usable_mas); + +/**   * uwb_rsv_find - find a reservation for a received DRP IE.   * @rc: the radio controller   * @src: source of the DRP IE @@ -611,8 +829,6 @@ static bool uwb_rsv_update_all(struct uwb_rc *rc)  	bool ie_updated = false;  	list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) { -		if (rsv->expired) -			uwb_drp_handle_timeout(rsv);  		if (!rsv->ie_valid) {  			uwb_drp_ie_update(rsv);  			ie_updated = true; @@ -622,9 +838,47 @@ static bool uwb_rsv_update_all(struct uwb_rc *rc)  	return ie_updated;  } +void uwb_rsv_queue_update(struct uwb_rc *rc) +{ +	unsigned long delay_us = UWB_MAS_LENGTH_US * UWB_MAS_PER_ZONE; + +	queue_delayed_work(rc->rsv_workq, &rc->rsv_update_work, usecs_to_jiffies(delay_us)); +} + +/** + * uwb_rsv_sched_update - schedule an update of the DRP IEs + * @rc: the radio controller. + * + * To improve performance and ensure correctness with [ECMA-368] the + * number of SET-DRP-IE commands that are done are limited. + * + * DRP IEs update come from two sources: DRP events from the hardware + * which all occur at the beginning of the superframe ('syncronous' + * events) and reservation establishment/termination requests from + * PALs or timers ('asynchronous' events). + * + * A delayed work ensures that all the synchronous events result in + * one SET-DRP-IE command. + * + * Additional logic (the set_drp_ie_pending and rsv_updated_postponed + * flags) will prevent an asynchrous event starting a SET-DRP-IE + * command if one is currently awaiting a response. + * + * FIXME: this does leave a window where an asynchrous event can delay + * the SET-DRP-IE for a synchronous event by one superframe. + */  void uwb_rsv_sched_update(struct uwb_rc *rc)  { -	queue_work(rc->rsv_workq, &rc->rsv_update_work); +	spin_lock(&rc->rsvs_lock); +	if (!delayed_work_pending(&rc->rsv_update_work)) { +		if (rc->set_drp_ie_pending > 0) { +			rc->set_drp_ie_pending++; +			goto unlock; +		} +		uwb_rsv_queue_update(rc); +	} +unlock: +	spin_unlock(&rc->rsvs_lock);  }  /* @@ -633,7 +887,8 @@ void uwb_rsv_sched_update(struct uwb_rc *rc)   */  static void uwb_rsv_update_work(struct work_struct *work)  { -	struct uwb_rc *rc = container_of(work, struct uwb_rc, rsv_update_work); +	struct uwb_rc *rc = container_of(work, struct uwb_rc, +					 rsv_update_work.work);  	bool ie_updated;  	mutex_lock(&rc->rsvs_mutex); @@ -645,18 +900,34 @@ static void uwb_rsv_update_work(struct work_struct *work)  		ie_updated = true;  	} -	if (ie_updated) +	if (ie_updated && (rc->set_drp_ie_pending == 0))  		uwb_rc_send_all_drp_ie(rc);  	mutex_unlock(&rc->rsvs_mutex);  } +static void uwb_rsv_alien_bp_work(struct work_struct *work) +{ +	struct uwb_rc *rc = container_of(work, struct uwb_rc, +					 rsv_alien_bp_work.work); +	struct uwb_rsv *rsv; + +	mutex_lock(&rc->rsvs_mutex); +	 +	list_for_each_entry(rsv, &rc->reservations, rc_node) { +		if (rsv->type != UWB_DRP_TYPE_ALIEN_BP) { +			rsv->callback(rsv); +		} +	} + +	mutex_unlock(&rc->rsvs_mutex); +} +  static void uwb_rsv_timer(unsigned long arg)  {  	struct uwb_rsv *rsv = (struct uwb_rsv *)arg; -	rsv->expired = true; -	uwb_rsv_sched_update(rsv->rc); +	queue_work(rsv->rc->rsv_workq, &rsv->handle_timeout_work);  }  /** @@ -673,16 +944,27 @@ void uwb_rsv_remove_all(struct uwb_rc *rc)  	list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {  		uwb_rsv_remove(rsv);  	} +	/* Cancel any postponed update. */ +	rc->set_drp_ie_pending = 0;  	mutex_unlock(&rc->rsvs_mutex); -	cancel_work_sync(&rc->rsv_update_work); +	cancel_delayed_work_sync(&rc->rsv_update_work);  }  void uwb_rsv_init(struct uwb_rc *rc)  {  	INIT_LIST_HEAD(&rc->reservations); +	INIT_LIST_HEAD(&rc->cnflt_alien_list);  	mutex_init(&rc->rsvs_mutex); -	INIT_WORK(&rc->rsv_update_work, uwb_rsv_update_work); +	spin_lock_init(&rc->rsvs_lock); +	INIT_DELAYED_WORK(&rc->rsv_update_work, uwb_rsv_update_work); +	INIT_DELAYED_WORK(&rc->rsv_alien_bp_work, uwb_rsv_alien_bp_work); +	rc->bow.can_reserve_extra_mases = true; +	rc->bow.total_expired = 0; +	rc->bow.window = UWB_DRP_BACKOFF_WIN_MIN >> 1; +	init_timer(&rc->bow.timer); +	rc->bow.timer.function = uwb_rsv_backoff_win_timer; +	rc->bow.timer.data     = (unsigned long)&rc->bow;  	bitmap_complement(rc->uwb_dev.streams, rc->uwb_dev.streams, UWB_NUM_STREAMS);  } diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c index a6debb9baf3..89b2e6a7214 100644 --- a/drivers/uwb/uwb-debug.c +++ b/drivers/uwb/uwb-debug.c @@ -82,29 +82,21 @@ struct uwb_dbg {  	struct dentry *reservations_f;  	struct dentry *accept_f;  	struct dentry *drp_avail_f; +	spinlock_t list_lock;  };  static struct dentry *root_dir;  static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv)  { -	struct uwb_rc *rc = rsv->rc; -	struct device *dev = &rc->uwb_dev.dev; -	struct uwb_dev_addr devaddr; -	char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE]; +	struct uwb_dbg *dbg = rsv->pal_priv; -	uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr); -	if (rsv->target.type == UWB_RSV_TARGET_DEV) -		devaddr = rsv->target.dev->dev_addr; -	else -		devaddr = rsv->target.devaddr; -	uwb_dev_addr_print(target, sizeof(target), &devaddr); - -	dev_dbg(dev, "debug: rsv %s -> %s: %s\n", -		owner, target, uwb_rsv_state_str(rsv->state)); +	uwb_rsv_dump("debug", rsv);  	if (rsv->state == UWB_RSV_STATE_NONE) { +		spin_lock(&dbg->list_lock);  		list_del(&rsv->pal_node); +		spin_unlock(&dbg->list_lock);  		uwb_rsv_destroy(rsv);  	}  } @@ -128,20 +120,21 @@ static int cmd_rsv_establish(struct uwb_rc *rc,  		return -ENOMEM;  	} -	rsv->owner       = &rc->uwb_dev; -	rsv->target.type = UWB_RSV_TARGET_DEV; -	rsv->target.dev  = target; -	rsv->type        = cmd->type; -	rsv->max_mas     = cmd->max_mas; -	rsv->min_mas     = cmd->min_mas; -	rsv->sparsity    = cmd->sparsity; +	rsv->target.type  = UWB_RSV_TARGET_DEV; +	rsv->target.dev   = target; +	rsv->type         = cmd->type; +	rsv->max_mas      = cmd->max_mas; +	rsv->min_mas      = cmd->min_mas; +	rsv->max_interval = cmd->max_interval;  	ret = uwb_rsv_establish(rsv);  	if (ret)  		uwb_rsv_destroy(rsv); -	else +	else { +		spin_lock(&(rc->dbg)->list_lock);  		list_add_tail(&rsv->pal_node, &rc->dbg->rsvs); - +		spin_unlock(&(rc->dbg)->list_lock); +	}  	return ret;  } @@ -151,17 +144,24 @@ static int cmd_rsv_terminate(struct uwb_rc *rc,  	struct uwb_rsv *rsv, *found = NULL;  	int i = 0; +	spin_lock(&(rc->dbg)->list_lock); +  	list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) {  		if (i == cmd->index) {  			found = rsv; +			uwb_rsv_get(found);  			break;  		}  		i++;  	} + +	spin_unlock(&(rc->dbg)->list_lock); +  	if (!found)  		return -EINVAL;  	uwb_rsv_terminate(found); +	uwb_rsv_put(found);  	return 0;  } @@ -191,7 +191,7 @@ static ssize_t command_write(struct file *file, const char __user *buf,  	struct uwb_rc *rc = file->private_data;  	struct uwb_dbg_cmd cmd;  	int ret = 0; - +	  	if (len != sizeof(struct uwb_dbg_cmd))  		return -EINVAL; @@ -325,7 +325,9 @@ static void uwb_dbg_new_rsv(struct uwb_pal *pal, struct uwb_rsv *rsv)  	struct uwb_dbg *dbg = container_of(pal, struct uwb_dbg, pal);  	if (dbg->accept) { +		spin_lock(&dbg->list_lock);  		list_add_tail(&rsv->pal_node, &dbg->rsvs); +		spin_unlock(&dbg->list_lock);  		uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, dbg);  	}  } @@ -341,6 +343,7 @@ void uwb_dbg_add_rc(struct uwb_rc *rc)  		return;  	INIT_LIST_HEAD(&rc->dbg->rsvs); +	spin_lock_init(&(rc->dbg)->list_lock);  	uwb_pal_init(&rc->dbg->pal);  	rc->dbg->pal.rc = rc; diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h index f0f21f406bf..d5bcfc1c227 100644 --- a/drivers/uwb/uwb-internal.h +++ b/drivers/uwb/uwb-internal.h @@ -92,6 +92,12 @@ extern const char *uwb_rc_strerror(unsigned code);  struct uwb_rc_neh; +extern int uwb_rc_cmd_async(struct uwb_rc *rc, const char *cmd_name, +			    struct uwb_rccb *cmd, size_t cmd_size, +			    u8 expected_type, u16 expected_event, +			    uwb_rc_cmd_cb_f cb, void *arg); + +  void uwb_rc_neh_create(struct uwb_rc *rc);  void uwb_rc_neh_destroy(struct uwb_rc *rc); @@ -106,7 +112,69 @@ void uwb_rc_neh_put(struct uwb_rc_neh *neh);  extern int uwb_est_create(void);  extern void uwb_est_destroy(void); +/* + * UWB conflicting alien reservations + */ +struct uwb_cnflt_alien { +	struct uwb_rc *rc; +	struct list_head rc_node; +	struct uwb_mas_bm mas; +	struct timer_list timer; +	struct work_struct cnflt_update_work; +}; + +enum uwb_uwb_rsv_alloc_result { +	UWB_RSV_ALLOC_FOUND = 0, +	UWB_RSV_ALLOC_NOT_FOUND, +}; + +enum uwb_rsv_mas_status { +	UWB_RSV_MAS_NOT_AVAIL = 1, +	UWB_RSV_MAS_SAFE, +	UWB_RSV_MAS_UNSAFE, +}; + +struct uwb_rsv_col_set_info { +	unsigned char start_col; +	unsigned char interval; +	unsigned char safe_mas_per_col; +	unsigned char unsafe_mas_per_col; +}; + +struct uwb_rsv_col_info { +	unsigned char max_avail_safe; +	unsigned char max_avail_unsafe; +	unsigned char highest_mas[UWB_MAS_PER_ZONE]; +	struct uwb_rsv_col_set_info csi; +}; + +struct uwb_rsv_row_info { +	unsigned char avail[UWB_MAS_PER_ZONE]; +	unsigned char free_rows; +	unsigned char used_rows; +}; + +/* + * UWB find allocation + */ +struct uwb_rsv_alloc_info { +	unsigned char bm[UWB_MAS_PER_ZONE * UWB_NUM_ZONES]; +	struct uwb_rsv_col_info ci[UWB_NUM_ZONES]; +	struct uwb_rsv_row_info ri; +	struct uwb_mas_bm *not_available; +	struct uwb_mas_bm *result; +	int min_mas; +	int max_mas; +	int max_interval; +	int total_allocated_mases; +	int safe_allocated_mases; +	int unsafe_allocated_mases; +	int interval; +}; +int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *available,  +				 struct uwb_mas_bm *result); +void uwb_rsv_handle_drp_avail_change(struct uwb_rc *rc);  /*   * UWB Events & management daemon   */ @@ -254,18 +322,28 @@ void uwb_rsv_init(struct uwb_rc *rc);  int uwb_rsv_setup(struct uwb_rc *rc);  void uwb_rsv_cleanup(struct uwb_rc *rc);  void uwb_rsv_remove_all(struct uwb_rc *rc); +void uwb_rsv_get(struct uwb_rsv *rsv); +void uwb_rsv_put(struct uwb_rsv *rsv); +bool uwb_rsv_has_two_drp_ies(struct uwb_rsv *rsv); +void uwb_rsv_dump(char *text, struct uwb_rsv *rsv); +int uwb_rsv_try_move(struct uwb_rsv *rsv, struct uwb_mas_bm *available); +void uwb_rsv_backoff_win_timer(unsigned long arg); +void uwb_rsv_backoff_win_increment(struct uwb_rc *rc); +int uwb_rsv_status(struct uwb_rsv *rsv); +int uwb_rsv_companion_status(struct uwb_rsv *rsv);  void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state);  void uwb_rsv_remove(struct uwb_rsv *rsv);  struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src,  			     struct uwb_ie_drp *drp_ie);  void uwb_rsv_sched_update(struct uwb_rc *rc); +void uwb_rsv_queue_update(struct uwb_rc *rc); -void uwb_drp_handle_timeout(struct uwb_rsv *rsv);  int uwb_drp_ie_update(struct uwb_rsv *rsv);  void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie);  void uwb_drp_avail_init(struct uwb_rc *rc); +void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail);  int  uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas);  void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas);  void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas); diff --git a/include/linux/uwb.h b/include/linux/uwb.h index d7ed5201ade..c02128991ff 100644 --- a/include/linux/uwb.h +++ b/include/linux/uwb.h @@ -67,6 +67,7 @@ struct uwb_dev {  	struct uwb_dev_addr dev_addr;  	int beacon_slot;  	DECLARE_BITMAP(streams, UWB_NUM_STREAMS); +	DECLARE_BITMAP(last_availability_bm, UWB_NUM_MAS);  };  #define to_uwb_dev(d) container_of(d, struct uwb_dev, dev) @@ -109,6 +110,9 @@ struct uwbd {   */  struct uwb_mas_bm {  	DECLARE_BITMAP(bm, UWB_NUM_MAS); +	DECLARE_BITMAP(unsafe_bm, UWB_NUM_MAS); +	int safe; +	int unsafe;  };  /** @@ -134,14 +138,24 @@ struct uwb_mas_bm {   * FIXME: further target states TBD.   */  enum uwb_rsv_state { -	UWB_RSV_STATE_NONE, +	UWB_RSV_STATE_NONE = 0,  	UWB_RSV_STATE_O_INITIATED,  	UWB_RSV_STATE_O_PENDING,  	UWB_RSV_STATE_O_MODIFIED,  	UWB_RSV_STATE_O_ESTABLISHED, +	UWB_RSV_STATE_O_TO_BE_MOVED, +	UWB_RSV_STATE_O_MOVE_EXPANDING, +	UWB_RSV_STATE_O_MOVE_COMBINING, +	UWB_RSV_STATE_O_MOVE_REDUCING,  	UWB_RSV_STATE_T_ACCEPTED,  	UWB_RSV_STATE_T_DENIED, +	UWB_RSV_STATE_T_CONFLICT,  	UWB_RSV_STATE_T_PENDING, +	UWB_RSV_STATE_T_EXPANDING_ACCEPTED, +	UWB_RSV_STATE_T_EXPANDING_CONFLICT, +	UWB_RSV_STATE_T_EXPANDING_PENDING, +	UWB_RSV_STATE_T_EXPANDING_DENIED, +	UWB_RSV_STATE_T_RESIZED,  	UWB_RSV_STATE_LAST,  }; @@ -166,6 +180,12 @@ struct uwb_rsv_target {  	};  }; +struct uwb_rsv_move { +	struct uwb_mas_bm final_mas; +	struct uwb_ie_drp *companion_drp_ie; +	struct uwb_mas_bm companion_mas; +}; +  /*   * Number of streams reserved for reservations targeted at DevAddrs.   */ @@ -203,6 +223,7 @@ typedef void (*uwb_rsv_cb_f)(struct uwb_rsv *rsv);   *   * @status:         negotiation status   * @stream:         stream index allocated for this reservation + * @tiebreaker:     conflict tiebreaker for this reservation   * @mas:            reserved MAS   * @drp_ie:         the DRP IE   * @ie_valid:       true iff the DRP IE matches the reservation parameters @@ -225,19 +246,22 @@ struct uwb_rsv {  	enum uwb_drp_type type;  	int max_mas;  	int min_mas; -	int sparsity; +	int max_interval;  	bool is_multicast;  	uwb_rsv_cb_f callback;  	void *pal_priv;  	enum uwb_rsv_state state; +	bool needs_release_companion_mas;  	u8 stream; +	u8 tiebreaker;  	struct uwb_mas_bm mas;  	struct uwb_ie_drp *drp_ie; +	struct uwb_rsv_move mv;  	bool ie_valid;  	struct timer_list timer; -	bool expired; +	struct work_struct handle_timeout_work;  };  static const @@ -279,6 +303,13 @@ struct uwb_drp_avail {  	bool ie_valid;  }; +struct uwb_drp_backoff_win { +	u8 window; +	u8 n; +	int total_expired; +	struct timer_list timer; +	bool can_reserve_extra_mases; +};  const char *uwb_rsv_state_str(enum uwb_rsv_state state);  const char *uwb_rsv_type_str(enum uwb_drp_type type); @@ -294,6 +325,8 @@ void uwb_rsv_terminate(struct uwb_rsv *rsv);  void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv); +void uwb_rsv_get_usable_mas(struct uwb_rsv *orig_rsv, struct uwb_mas_bm *mas); +  /**   * Radio Control Interface instance   * @@ -364,12 +397,18 @@ struct uwb_rc {  	struct uwbd uwbd; +	struct uwb_drp_backoff_win bow;  	struct uwb_drp_avail drp_avail;  	struct list_head reservations; +	struct list_head cnflt_alien_list; +	struct uwb_mas_bm cnflt_alien_bitmap;  	struct mutex rsvs_mutex; +	spinlock_t rsvs_lock;  	struct workqueue_struct *rsv_workq; -	struct work_struct rsv_update_work; +	struct delayed_work rsv_update_work; +	struct delayed_work rsv_alien_bp_work; +	int set_drp_ie_pending;  	struct mutex ies_mutex;  	struct uwb_rc_cmd_set_ie *ies;  	size_t ies_capacity; diff --git a/include/linux/uwb/debug-cmd.h b/include/linux/uwb/debug-cmd.h index 07efbe17db5..8da004e2562 100644 --- a/include/linux/uwb/debug-cmd.h +++ b/include/linux/uwb/debug-cmd.h @@ -43,7 +43,7 @@ struct uwb_dbg_cmd_rsv_establish {  	__u8  type;  	__u16 max_mas;  	__u16 min_mas; -	__u8  sparsity; +	__u8  max_interval;  };  struct uwb_dbg_cmd_rsv_terminate { diff --git a/include/linux/uwb/spec.h b/include/linux/uwb/spec.h index a30436ea53a..b52e44f1bd3 100644 --- a/include/linux/uwb/spec.h +++ b/include/linux/uwb/spec.h @@ -59,6 +59,11 @@ enum { UWB_NUM_ZONES = 16 };  #define UWB_MAS_PER_ZONE (UWB_NUM_MAS / UWB_NUM_ZONES)  /* + * Number of MAS required before a row can be considered available. + */ +#define UWB_USABLE_MAS_PER_ROW (UWB_NUM_ZONES - 1) + +/*   * Number of streams per DRP reservation between a pair of devices.   *   * [ECMA-368] section 16.8.6. @@ -94,6 +99,26 @@ enum { UWB_BEACON_SLOT_LENGTH_US = 85 };  enum { UWB_MAX_LOST_BEACONS = 3 };  /* + * mDRPBackOffWinMin + * + * The minimum number of superframes to wait before trying to reserve + * extra MAS. + * + * [ECMA-368] section 17.16 + */ +enum { UWB_DRP_BACKOFF_WIN_MIN = 2 }; + +/* + * mDRPBackOffWinMax + * + * The maximum number of superframes to wait before trying to reserve + * extra MAS. + * + * [ECMA-368] section 17.16 + */ +enum { UWB_DRP_BACKOFF_WIN_MAX = 16 }; + +/*   * Length of a superframe in microseconds.   */  #define UWB_SUPERFRAME_LENGTH_US (UWB_MAS_LENGTH_US * UWB_NUM_MAS)  |