forked from mirrors/linux
		
	uwb: add the UWB stack (core files)
UWB device and radio controller device and event management. Signed-off-by: David Vrabel <david.vrabel@csr.com>
This commit is contained in:
		
							parent
							
								
									34e95e41f1
								
							
						
					
					
						commit
						183b9b592a
					
				
					 6 changed files with 1939 additions and 0 deletions
				
			
		
							
								
								
									
										142
									
								
								drivers/uwb/driver.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								drivers/uwb/driver.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,142 @@ | |||
| /*
 | ||||
|  * Ultra Wide Band | ||||
|  * Driver initialization, etc | ||||
|  * | ||||
|  * Copyright (C) 2005-2006 Intel Corporation | ||||
|  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||||
|  * | ||||
|  * 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, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
|  * 02110-1301, USA. | ||||
|  * | ||||
|  * | ||||
|  * FIXME: docs | ||||
|  * | ||||
|  * Life cycle: FIXME: explain | ||||
|  * | ||||
|  *  UWB radio controller: | ||||
|  * | ||||
|  *    1. alloc a uwb_rc, zero it | ||||
|  *    2. call uwb_rc_init() on it to set it up + ops (won't do any | ||||
|  *       kind of allocation) | ||||
|  *    3. register (now it is owned by the UWB stack--deregister before | ||||
|  *       freeing/destroying). | ||||
|  *    4. It lives on it's own now (UWB stack handles)--when it | ||||
|  *       disconnects, call unregister() | ||||
|  *    5. free it. | ||||
|  * | ||||
|  *    Make sure you have a reference to the uwb_rc before calling | ||||
|  *    any of the UWB API functions. | ||||
|  * | ||||
|  * TODO: | ||||
|  * | ||||
|  * 1. Locking and life cycle management is crappy still. All entry | ||||
|  *    points to the UWB HCD API assume you have a reference on the | ||||
|  *    uwb_rc structure and that it won't go away. They mutex lock it | ||||
|  *    before doing anything. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/kdev_t.h> | ||||
| #include <linux/random.h> | ||||
| #include <linux/uwb/debug.h> | ||||
| #include "uwb-internal.h" | ||||
| 
 | ||||
| 
 | ||||
| /* UWB stack attributes (or 'global' constants) */ | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * If a beacon dissapears for longer than this, then we consider the | ||||
|  * device who was represented by that beacon to be gone. | ||||
|  * | ||||
|  * ECMA-368[17.2.3, last para] establishes that a device must not | ||||
|  * consider a device to be its neighbour if he doesn't receive a beacon | ||||
|  * for more than mMaxLostBeacons. mMaxLostBeacons is defined in | ||||
|  * ECMA-368[17.16] as 3; because we can get only one beacon per | ||||
|  * superframe, that'd be 3 * 65ms = 195 ~ 200 ms. Let's give it time | ||||
|  * for jitter and stuff and make it 500 ms. | ||||
|  */ | ||||
| unsigned long beacon_timeout_ms = 500; | ||||
| 
 | ||||
| static | ||||
| ssize_t beacon_timeout_ms_show(struct class *class, char *buf) | ||||
| { | ||||
| 	return scnprintf(buf, PAGE_SIZE, "%lu\n", beacon_timeout_ms); | ||||
| } | ||||
| 
 | ||||
| static | ||||
| ssize_t beacon_timeout_ms_store(struct class *class, | ||||
| 				const char *buf, size_t size) | ||||
| { | ||||
| 	unsigned long bt; | ||||
| 	ssize_t result; | ||||
| 	result = sscanf(buf, "%lu", &bt); | ||||
| 	if (result != 1) | ||||
| 		return -EINVAL; | ||||
| 	beacon_timeout_ms = bt; | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static struct class_attribute uwb_class_attrs[] = { | ||||
| 	__ATTR(beacon_timeout_ms, S_IWUSR | S_IRUGO, | ||||
| 	       beacon_timeout_ms_show, beacon_timeout_ms_store), | ||||
| 	__ATTR_NULL, | ||||
| }; | ||||
| 
 | ||||
| /** Device model classes */ | ||||
| struct class uwb_rc_class = { | ||||
| 	.name        = "uwb_rc", | ||||
| 	.class_attrs = uwb_class_attrs, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static int __init uwb_subsys_init(void) | ||||
| { | ||||
| 	int result = 0; | ||||
| 
 | ||||
| 	result = uwb_est_create(); | ||||
| 	if (result < 0) { | ||||
| 		printk(KERN_ERR "uwb: Can't initialize EST subsystem\n"); | ||||
| 		goto error_est_init; | ||||
| 	} | ||||
| 
 | ||||
| 	result = class_register(&uwb_rc_class); | ||||
| 	if (result < 0) | ||||
| 		goto error_uwb_rc_class_register; | ||||
| 	uwbd_start(); | ||||
| 	return 0; | ||||
| 
 | ||||
| error_uwb_rc_class_register: | ||||
| 	uwb_est_destroy(); | ||||
| error_est_init: | ||||
| 	return result; | ||||
| } | ||||
| module_init(uwb_subsys_init); | ||||
| 
 | ||||
| static void __exit uwb_subsys_exit(void) | ||||
| { | ||||
| 	uwbd_stop(); | ||||
| 	class_unregister(&uwb_rc_class); | ||||
| 	uwb_est_destroy(); | ||||
| 	return; | ||||
| } | ||||
| module_exit(uwb_subsys_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); | ||||
| MODULE_DESCRIPTION("Ultra Wide Band core"); | ||||
| MODULE_LICENSE("GPL"); | ||||
							
								
								
									
										492
									
								
								drivers/uwb/lc-dev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										492
									
								
								drivers/uwb/lc-dev.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,492 @@ | |||
| /*
 | ||||
|  * Ultra Wide Band | ||||
|  * Life cycle of devices | ||||
|  * | ||||
|  * Copyright (C) 2005-2006 Intel Corporation | ||||
|  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||||
|  * | ||||
|  * 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, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
|  * 02110-1301, USA. | ||||
|  * | ||||
|  * | ||||
|  * FIXME: docs | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/kdev_t.h> | ||||
| #include <linux/random.h> | ||||
| #include "uwb-internal.h" | ||||
| 
 | ||||
| #define D_LOCAL 1 | ||||
| #include <linux/uwb/debug.h> | ||||
| 
 | ||||
| 
 | ||||
| /* We initialize addresses to 0xff (invalid, as it is bcast) */ | ||||
| static inline void uwb_dev_addr_init(struct uwb_dev_addr *addr) | ||||
| { | ||||
| 	memset(&addr->data, 0xff, sizeof(addr->data)); | ||||
| } | ||||
| 
 | ||||
| static inline void uwb_mac_addr_init(struct uwb_mac_addr *addr) | ||||
| { | ||||
| 	memset(&addr->data, 0xff, sizeof(addr->data)); | ||||
| } | ||||
| 
 | ||||
| /* @returns !0 if a device @addr is a broadcast address */ | ||||
| static inline int uwb_dev_addr_bcast(const struct uwb_dev_addr *addr) | ||||
| { | ||||
| 	static const struct uwb_dev_addr bcast = { .data = { 0xff, 0xff } }; | ||||
| 	return !uwb_dev_addr_cmp(addr, &bcast); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Add callback @new to be called when an event occurs in @rc. | ||||
|  */ | ||||
| int uwb_notifs_register(struct uwb_rc *rc, struct uwb_notifs_handler *new) | ||||
| { | ||||
| 	if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) | ||||
| 		return -ERESTARTSYS; | ||||
| 	list_add(&new->list_node, &rc->notifs_chain.list); | ||||
| 	mutex_unlock(&rc->notifs_chain.mutex); | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_notifs_register); | ||||
| 
 | ||||
| /*
 | ||||
|  * Remove event handler (callback) | ||||
|  */ | ||||
| int uwb_notifs_deregister(struct uwb_rc *rc, struct uwb_notifs_handler *entry) | ||||
| { | ||||
| 	if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) | ||||
| 		return -ERESTARTSYS; | ||||
| 	list_del(&entry->list_node); | ||||
| 	mutex_unlock(&rc->notifs_chain.mutex); | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_notifs_deregister); | ||||
| 
 | ||||
| /*
 | ||||
|  * Notify all event handlers of a given event on @rc | ||||
|  * | ||||
|  * We are called with a valid reference to the device, or NULL if the | ||||
|  * event is not for a particular event (e.g., a BG join event). | ||||
|  */ | ||||
| void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event) | ||||
| { | ||||
| 	struct uwb_notifs_handler *handler; | ||||
| 	if (mutex_lock_interruptible(&rc->notifs_chain.mutex)) | ||||
| 		return; | ||||
| 	if (!list_empty(&rc->notifs_chain.list)) { | ||||
| 		list_for_each_entry(handler, &rc->notifs_chain.list, list_node) { | ||||
| 			handler->cb(handler->data, uwb_dev, event); | ||||
| 		} | ||||
| 	} | ||||
| 	mutex_unlock(&rc->notifs_chain.mutex); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Release the backing device of a uwb_dev that has been dynamically allocated. | ||||
|  */ | ||||
| static void uwb_dev_sys_release(struct device *dev) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 
 | ||||
| 	d_fnstart(4, NULL, "(dev %p uwb_dev %p)\n", dev, uwb_dev); | ||||
| 	uwb_bce_put(uwb_dev->bce); | ||||
| 	d_printf(0, &uwb_dev->dev, "uwb_dev %p freed\n", uwb_dev); | ||||
| 	memset(uwb_dev, 0x69, sizeof(*uwb_dev)); | ||||
| 	kfree(uwb_dev); | ||||
| 	d_fnend(4, NULL, "(dev %p uwb_dev %p) = void\n", dev, uwb_dev); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Initialize a UWB device instance | ||||
|  * | ||||
|  * Alloc, zero and call this function. | ||||
|  */ | ||||
| void uwb_dev_init(struct uwb_dev *uwb_dev) | ||||
| { | ||||
| 	mutex_init(&uwb_dev->mutex); | ||||
| 	device_initialize(&uwb_dev->dev); | ||||
| 	uwb_dev->dev.release = uwb_dev_sys_release; | ||||
| 	uwb_dev_addr_init(&uwb_dev->dev_addr); | ||||
| 	uwb_mac_addr_init(&uwb_dev->mac_addr); | ||||
| 	bitmap_fill(uwb_dev->streams, UWB_NUM_GLOBAL_STREAMS); | ||||
| } | ||||
| 
 | ||||
| static ssize_t uwb_dev_EUI_48_show(struct device *dev, | ||||
| 				   struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	char addr[UWB_ADDR_STRSIZE]; | ||||
| 
 | ||||
| 	uwb_mac_addr_print(addr, sizeof(addr), &uwb_dev->mac_addr); | ||||
| 	return sprintf(buf, "%s\n", addr); | ||||
| } | ||||
| static DEVICE_ATTR(EUI_48, S_IRUGO, uwb_dev_EUI_48_show, NULL); | ||||
| 
 | ||||
| static ssize_t uwb_dev_DevAddr_show(struct device *dev, | ||||
| 				    struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	char addr[UWB_ADDR_STRSIZE]; | ||||
| 
 | ||||
| 	uwb_dev_addr_print(addr, sizeof(addr), &uwb_dev->dev_addr); | ||||
| 	return sprintf(buf, "%s\n", addr); | ||||
| } | ||||
| static DEVICE_ATTR(DevAddr, S_IRUGO, uwb_dev_DevAddr_show, NULL); | ||||
| 
 | ||||
| /*
 | ||||
|  * Show the BPST of this device. | ||||
|  * | ||||
|  * Calculated from the receive time of the device's beacon and it's | ||||
|  * slot number. | ||||
|  */ | ||||
| static ssize_t uwb_dev_BPST_show(struct device *dev, | ||||
| 				 struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	struct uwb_beca_e *bce; | ||||
| 	struct uwb_beacon_frame *bf; | ||||
| 	u16 bpst; | ||||
| 
 | ||||
| 	bce = uwb_dev->bce; | ||||
| 	mutex_lock(&bce->mutex); | ||||
| 	bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo; | ||||
| 	bpst = bce->be->wBPSTOffset | ||||
| 		- (u16)(bf->Beacon_Slot_Number * UWB_BEACON_SLOT_LENGTH_US); | ||||
| 	mutex_unlock(&bce->mutex); | ||||
| 
 | ||||
| 	return sprintf(buf, "%d\n", bpst); | ||||
| } | ||||
| static DEVICE_ATTR(BPST, S_IRUGO, uwb_dev_BPST_show, NULL); | ||||
| 
 | ||||
| /*
 | ||||
|  * Show the IEs a device is beaconing | ||||
|  * | ||||
|  * We need to access the beacon cache, so we just lock it really | ||||
|  * quick, print the IEs and unlock. | ||||
|  * | ||||
|  * We have a reference on the cache entry, so that should be | ||||
|  * quite safe. | ||||
|  */ | ||||
| static ssize_t uwb_dev_IEs_show(struct device *dev, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 
 | ||||
| 	return uwb_bce_print_IEs(uwb_dev, uwb_dev->bce, buf, PAGE_SIZE); | ||||
| } | ||||
| static DEVICE_ATTR(IEs, S_IRUGO | S_IWUSR, uwb_dev_IEs_show, NULL); | ||||
| 
 | ||||
| static ssize_t uwb_dev_LQE_show(struct device *dev, | ||||
| 				struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	struct uwb_beca_e *bce = uwb_dev->bce; | ||||
| 	size_t result; | ||||
| 
 | ||||
| 	mutex_lock(&bce->mutex); | ||||
| 	result = stats_show(&uwb_dev->bce->lqe_stats, buf); | ||||
| 	mutex_unlock(&bce->mutex); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| static ssize_t uwb_dev_LQE_store(struct device *dev, | ||||
| 				 struct device_attribute *attr, | ||||
| 				 const char *buf, size_t size) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	struct uwb_beca_e *bce = uwb_dev->bce; | ||||
| 	ssize_t result; | ||||
| 
 | ||||
| 	mutex_lock(&bce->mutex); | ||||
| 	result = stats_store(&uwb_dev->bce->lqe_stats, buf, size); | ||||
| 	mutex_unlock(&bce->mutex); | ||||
| 	return result; | ||||
| } | ||||
| static DEVICE_ATTR(LQE, S_IRUGO | S_IWUSR, uwb_dev_LQE_show, uwb_dev_LQE_store); | ||||
| 
 | ||||
| static ssize_t uwb_dev_RSSI_show(struct device *dev, | ||||
| 				 struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	struct uwb_beca_e *bce = uwb_dev->bce; | ||||
| 	size_t result; | ||||
| 
 | ||||
| 	mutex_lock(&bce->mutex); | ||||
| 	result = stats_show(&uwb_dev->bce->rssi_stats, buf); | ||||
| 	mutex_unlock(&bce->mutex); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| static ssize_t uwb_dev_RSSI_store(struct device *dev, | ||||
| 				  struct device_attribute *attr, | ||||
| 				  const char *buf, size_t size) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	struct uwb_beca_e *bce = uwb_dev->bce; | ||||
| 	ssize_t result; | ||||
| 
 | ||||
| 	mutex_lock(&bce->mutex); | ||||
| 	result = stats_store(&uwb_dev->bce->rssi_stats, buf, size); | ||||
| 	mutex_unlock(&bce->mutex); | ||||
| 	return result; | ||||
| } | ||||
| static DEVICE_ATTR(RSSI, S_IRUGO | S_IWUSR, uwb_dev_RSSI_show, uwb_dev_RSSI_store); | ||||
| 
 | ||||
| 
 | ||||
| static struct attribute *dev_attrs[] = { | ||||
| 	&dev_attr_EUI_48.attr, | ||||
| 	&dev_attr_DevAddr.attr, | ||||
| 	&dev_attr_BPST.attr, | ||||
| 	&dev_attr_IEs.attr, | ||||
| 	&dev_attr_LQE.attr, | ||||
| 	&dev_attr_RSSI.attr, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| static struct attribute_group dev_attr_group = { | ||||
| 	.attrs = dev_attrs, | ||||
| }; | ||||
| 
 | ||||
| static struct attribute_group *groups[] = { | ||||
| 	&dev_attr_group, | ||||
| 	NULL, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Device SYSFS registration | ||||
|  * | ||||
|  * | ||||
|  */ | ||||
| static int __uwb_dev_sys_add(struct uwb_dev *uwb_dev, struct device *parent_dev) | ||||
| { | ||||
| 	int result; | ||||
| 	struct device *dev; | ||||
| 
 | ||||
| 	d_fnstart(4, NULL, "(uwb_dev %p parent_dev %p)\n", uwb_dev, parent_dev); | ||||
| 	BUG_ON(parent_dev == NULL); | ||||
| 
 | ||||
| 	dev = &uwb_dev->dev; | ||||
| 	/* Device sysfs files are only useful for neighbor devices not
 | ||||
| 	   local radio controllers. */ | ||||
| 	if (&uwb_dev->rc->uwb_dev != uwb_dev) | ||||
| 		dev->groups = groups; | ||||
| 	dev->parent = parent_dev; | ||||
| 	dev_set_drvdata(dev, uwb_dev); | ||||
| 
 | ||||
| 	result = device_add(dev); | ||||
| 	d_fnend(4, NULL, "(uwb_dev %p parent_dev %p) = %d\n", uwb_dev, parent_dev, result); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void __uwb_dev_sys_rm(struct uwb_dev *uwb_dev) | ||||
| { | ||||
| 	d_fnstart(4, NULL, "(uwb_dev %p)\n", uwb_dev); | ||||
| 	dev_set_drvdata(&uwb_dev->dev, NULL); | ||||
| 	device_del(&uwb_dev->dev); | ||||
| 	d_fnend(4, NULL, "(uwb_dev %p) = void\n", uwb_dev); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Register and initialize a new UWB device | ||||
|  * | ||||
|  * Did you call uwb_dev_init() on it? | ||||
|  * | ||||
|  * @parent_rc: is the parent radio controller who has the link to the | ||||
|  *             device. When registering the UWB device that is a UWB | ||||
|  *             Radio Controller, we point back to it. | ||||
|  * | ||||
|  * If registering the device that is part of a radio, caller has set | ||||
|  * rc->uwb_dev->dev. Otherwise it is to be left NULL--a new one will | ||||
|  * be allocated. | ||||
|  */ | ||||
| int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev, | ||||
| 		struct uwb_rc *parent_rc) | ||||
| { | ||||
| 	int result; | ||||
| 	struct device *dev; | ||||
| 
 | ||||
| 	BUG_ON(uwb_dev == NULL); | ||||
| 	BUG_ON(parent_dev == NULL); | ||||
| 	BUG_ON(parent_rc == NULL); | ||||
| 
 | ||||
| 	mutex_lock(&uwb_dev->mutex); | ||||
| 	dev = &uwb_dev->dev; | ||||
| 	uwb_dev->rc = parent_rc; | ||||
| 	result = __uwb_dev_sys_add(uwb_dev, parent_dev); | ||||
| 	if (result < 0) | ||||
| 		printk(KERN_ERR "UWB: unable to register dev %s with sysfs: %d\n", | ||||
| 		       dev_name(dev), result); | ||||
| 	mutex_unlock(&uwb_dev->mutex); | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void uwb_dev_rm(struct uwb_dev *uwb_dev) | ||||
| { | ||||
| 	mutex_lock(&uwb_dev->mutex); | ||||
| 	__uwb_dev_sys_rm(uwb_dev); | ||||
| 	mutex_unlock(&uwb_dev->mutex); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static | ||||
| int __uwb_dev_try_get(struct device *dev, void *__target_uwb_dev) | ||||
| { | ||||
| 	struct uwb_dev *target_uwb_dev = __target_uwb_dev; | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 	if (uwb_dev == target_uwb_dev) { | ||||
| 		uwb_dev_get(uwb_dev); | ||||
| 		return 1; | ||||
| 	} else | ||||
| 		return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Given a UWB device descriptor, validate and refcount it | ||||
|  * | ||||
|  * @returns NULL if the device does not exist or is quiescing; the ptr to | ||||
|  *               it otherwise. | ||||
|  */ | ||||
| struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev) | ||||
| { | ||||
| 	if (uwb_dev_for_each(rc, __uwb_dev_try_get, uwb_dev)) | ||||
| 		return uwb_dev; | ||||
| 	else | ||||
| 		return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_dev_try_get); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Remove a device from the system [grunt for other functions] | ||||
|  */ | ||||
| int __uwb_dev_offair(struct uwb_dev *uwb_dev, struct uwb_rc *rc) | ||||
| { | ||||
| 	struct device *dev = &uwb_dev->dev; | ||||
| 	char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; | ||||
| 
 | ||||
| 	d_fnstart(3, NULL, "(dev %p [uwb_dev %p], uwb_rc %p)\n", dev, uwb_dev, rc); | ||||
| 	uwb_mac_addr_print(macbuf, sizeof(macbuf), &uwb_dev->mac_addr); | ||||
| 	uwb_dev_addr_print(devbuf, sizeof(devbuf), &uwb_dev->dev_addr); | ||||
| 	dev_info(dev, "uwb device (mac %s dev %s) disconnected from %s %s\n", | ||||
| 		 macbuf, devbuf, | ||||
| 		 rc ? rc->uwb_dev.dev.parent->bus->name : "n/a", | ||||
| 		 rc ? dev_name(rc->uwb_dev.dev.parent) : ""); | ||||
| 	uwb_dev_rm(uwb_dev); | ||||
| 	uwb_dev_put(uwb_dev);	/* for the creation in _onair() */ | ||||
| 	d_fnend(3, NULL, "(dev %p [uwb_dev %p], uwb_rc %p) = 0\n", dev, uwb_dev, rc); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * A device went off the air, clean up after it! | ||||
|  * | ||||
|  * This is called by the UWB Daemon (through the beacon purge function | ||||
|  * uwb_bcn_cache_purge) when it is detected that a device has been in | ||||
|  * radio silence for a while. | ||||
|  * | ||||
|  * If this device is actually a local radio controller we don't need | ||||
|  * to go through the offair process, as it is not registered as that. | ||||
|  * | ||||
|  * NOTE: uwb_bcn_cache.mutex is held! | ||||
|  */ | ||||
| void uwbd_dev_offair(struct uwb_beca_e *bce) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev; | ||||
| 
 | ||||
| 	uwb_dev = bce->uwb_dev; | ||||
| 	if (uwb_dev) { | ||||
| 		uwb_notify(uwb_dev->rc, uwb_dev, UWB_NOTIF_OFFAIR); | ||||
| 		__uwb_dev_offair(uwb_dev, uwb_dev->rc); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * A device went on the air, start it up! | ||||
|  * | ||||
|  * This is called by the UWB Daemon when it is detected that a device | ||||
|  * has popped up in the radio range of the radio controller. | ||||
|  * | ||||
|  * It will just create the freaking device, register the beacon and | ||||
|  * stuff and yatla, done. | ||||
|  * | ||||
|  * | ||||
|  * NOTE: uwb_beca.mutex is held, bce->mutex is held | ||||
|  */ | ||||
| void uwbd_dev_onair(struct uwb_rc *rc, struct uwb_beca_e *bce) | ||||
| { | ||||
| 	int result; | ||||
| 	struct device *dev = &rc->uwb_dev.dev; | ||||
| 	struct uwb_dev *uwb_dev; | ||||
| 	char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; | ||||
| 
 | ||||
| 	uwb_mac_addr_print(macbuf, sizeof(macbuf), bce->mac_addr); | ||||
| 	uwb_dev_addr_print(devbuf, sizeof(devbuf), &bce->dev_addr); | ||||
| 	uwb_dev = kcalloc(1, sizeof(*uwb_dev), GFP_KERNEL); | ||||
| 	if (uwb_dev == NULL) { | ||||
| 		dev_err(dev, "new device %s: Cannot allocate memory\n", | ||||
| 			macbuf); | ||||
| 		return; | ||||
| 	} | ||||
| 	uwb_dev_init(uwb_dev);		/* This sets refcnt to one, we own it */ | ||||
| 	uwb_dev->mac_addr = *bce->mac_addr; | ||||
| 	uwb_dev->dev_addr = bce->dev_addr; | ||||
| 	dev_set_name(&uwb_dev->dev, macbuf); | ||||
| 	result = uwb_dev_add(uwb_dev, &rc->uwb_dev.dev, rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "new device %s: cannot instantiate device\n", | ||||
| 			macbuf); | ||||
| 		goto error_dev_add; | ||||
| 	} | ||||
| 	/* plug the beacon cache */ | ||||
| 	bce->uwb_dev = uwb_dev; | ||||
| 	uwb_dev->bce = bce; | ||||
| 	uwb_bce_get(bce);		/* released in uwb_dev_sys_release() */ | ||||
| 	dev_info(dev, "uwb device (mac %s dev %s) connected to %s %s\n", | ||||
| 		 macbuf, devbuf, rc->uwb_dev.dev.parent->bus->name, | ||||
| 		 dev_name(rc->uwb_dev.dev.parent)); | ||||
| 	uwb_notify(rc, uwb_dev, UWB_NOTIF_ONAIR); | ||||
| 	return; | ||||
| 
 | ||||
| error_dev_add: | ||||
| 	kfree(uwb_dev); | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Iterate over the list of UWB devices, calling a @function on each | ||||
|  * | ||||
|  * See docs for bus_for_each().... | ||||
|  * | ||||
|  * @rc:       radio controller for the devices. | ||||
|  * @function: function to call. | ||||
|  * @priv:     data to pass to @function. | ||||
|  * @returns:  0 if no invocation of function() returned a value | ||||
|  *            different to zero. That value otherwise. | ||||
|  */ | ||||
| int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f function, void *priv) | ||||
| { | ||||
| 	return device_for_each_child(&rc->uwb_dev.dev, priv, function); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_dev_for_each); | ||||
							
								
								
									
										501
									
								
								drivers/uwb/lc-rc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										501
									
								
								drivers/uwb/lc-rc.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,501 @@ | |||
| /*
 | ||||
|  * Ultra Wide Band | ||||
|  * Life cycle of radio controllers | ||||
|  * | ||||
|  * Copyright (C) 2005-2006 Intel Corporation | ||||
|  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||||
|  * | ||||
|  * 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, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
|  * 02110-1301, USA. | ||||
|  * | ||||
|  * | ||||
|  * FIXME: docs | ||||
|  * | ||||
|  * A UWB radio controller is also a UWB device, so it embeds one... | ||||
|  * | ||||
|  * List of RCs comes from the 'struct class uwb_rc_class'. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/random.h> | ||||
| #include <linux/kdev_t.h> | ||||
| #include <linux/etherdevice.h> | ||||
| #include <linux/usb.h> | ||||
| 
 | ||||
| #define D_LOCAL 1 | ||||
| #include <linux/uwb/debug.h> | ||||
| #include "uwb-internal.h" | ||||
| 
 | ||||
| static int uwb_rc_index_match(struct device *dev, void *data) | ||||
| { | ||||
| 	int *index = data; | ||||
| 	struct uwb_rc *rc = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	if (rc->index == *index) | ||||
| 		return 1; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct uwb_rc *uwb_rc_find_by_index(int index) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 	struct uwb_rc *rc = NULL; | ||||
| 
 | ||||
| 	dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match); | ||||
| 	if (dev) | ||||
| 		rc = dev_get_drvdata(dev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int uwb_rc_new_index(void) | ||||
| { | ||||
| 	int index = 0; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		if (!uwb_rc_find_by_index(index)) | ||||
| 			return index; | ||||
| 		if (++index < 0) | ||||
| 			index = 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Release the backing device of a uwb_rc that has been dynamically allocated. | ||||
|  */ | ||||
| static void uwb_rc_sys_release(struct device *dev) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = container_of(dev, struct uwb_dev, dev); | ||||
| 	struct uwb_rc *rc = container_of(uwb_dev, struct uwb_rc, uwb_dev); | ||||
| 
 | ||||
| 	uwb_rc_neh_destroy(rc); | ||||
| 	uwb_rc_ie_release(rc); | ||||
| 	d_printf(1, dev, "freed uwb_rc %p\n", rc); | ||||
| 	kfree(rc); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void uwb_rc_init(struct uwb_rc *rc) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = &rc->uwb_dev; | ||||
| 
 | ||||
| 	uwb_dev_init(uwb_dev); | ||||
| 	rc->uwb_dev.dev.class = &uwb_rc_class; | ||||
| 	rc->uwb_dev.dev.release = uwb_rc_sys_release; | ||||
| 	uwb_rc_neh_create(rc); | ||||
| 	rc->beaconing = -1; | ||||
| 	rc->scan_type = UWB_SCAN_DISABLED; | ||||
| 	INIT_LIST_HEAD(&rc->notifs_chain.list); | ||||
| 	mutex_init(&rc->notifs_chain.mutex); | ||||
| 	uwb_drp_avail_init(rc); | ||||
| 	uwb_rc_ie_init(rc); | ||||
| 	uwb_rsv_init(rc); | ||||
| 	uwb_rc_pal_init(rc); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_rc_init); | ||||
| 
 | ||||
| 
 | ||||
| struct uwb_rc *uwb_rc_alloc(void) | ||||
| { | ||||
| 	struct uwb_rc *rc; | ||||
| 	rc = kzalloc(sizeof(*rc), GFP_KERNEL); | ||||
| 	if (rc == NULL) | ||||
| 		return NULL; | ||||
| 	uwb_rc_init(rc); | ||||
| 	return rc; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_rc_alloc); | ||||
| 
 | ||||
| static struct attribute *rc_attrs[] = { | ||||
| 		&dev_attr_mac_address.attr, | ||||
| 		&dev_attr_scan.attr, | ||||
| 		&dev_attr_beacon.attr, | ||||
| 		NULL, | ||||
| }; | ||||
| 
 | ||||
| static struct attribute_group rc_attr_group = { | ||||
| 	.attrs = rc_attrs, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Registration of sysfs specific stuff | ||||
|  */ | ||||
| static int uwb_rc_sys_add(struct uwb_rc *rc) | ||||
| { | ||||
| 	return sysfs_create_group(&rc->uwb_dev.dev.kobj, &rc_attr_group); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void __uwb_rc_sys_rm(struct uwb_rc *rc) | ||||
| { | ||||
| 	sysfs_remove_group(&rc->uwb_dev.dev.kobj, &rc_attr_group); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * uwb_rc_mac_addr_setup - get an RC's EUI-48 address or set it | ||||
|  * @rc:  the radio controller. | ||||
|  * | ||||
|  * If the EUI-48 address is 00:00:00:00:00:00 or FF:FF:FF:FF:FF:FF | ||||
|  * then a random locally administered EUI-48 is generated and set on | ||||
|  * the device.  The probability of address collisions is sufficiently | ||||
|  * unlikely (1/2^40 = 9.1e-13) that they're not checked for. | ||||
|  */ | ||||
| static | ||||
| int uwb_rc_mac_addr_setup(struct uwb_rc *rc) | ||||
| { | ||||
| 	int result; | ||||
| 	struct device *dev = &rc->uwb_dev.dev; | ||||
| 	struct uwb_dev *uwb_dev = &rc->uwb_dev; | ||||
| 	char devname[UWB_ADDR_STRSIZE]; | ||||
| 	struct uwb_mac_addr addr; | ||||
| 
 | ||||
| 	result = uwb_rc_mac_addr_get(rc, &addr); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot retrieve UWB EUI-48 address: %d\n", result); | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	if (uwb_mac_addr_unset(&addr) || uwb_mac_addr_bcast(&addr)) { | ||||
| 		addr.data[0] = 0x02; /* locally adminstered and unicast */ | ||||
| 		get_random_bytes(&addr.data[1], sizeof(addr.data)-1); | ||||
| 
 | ||||
| 		result = uwb_rc_mac_addr_set(rc, &addr); | ||||
| 		if (result < 0) { | ||||
| 			uwb_mac_addr_print(devname, sizeof(devname), &addr); | ||||
| 			dev_err(dev, "cannot set EUI-48 address %s: %d\n", | ||||
| 				devname, result); | ||||
| 			return result; | ||||
| 		} | ||||
| 	} | ||||
| 	uwb_dev->mac_addr = addr; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static int uwb_rc_setup(struct uwb_rc *rc) | ||||
| { | ||||
| 	int result; | ||||
| 	struct device *dev = &rc->uwb_dev.dev; | ||||
| 
 | ||||
| 	result = uwb_rc_reset(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot reset UWB radio: %d\n", result); | ||||
| 		goto error; | ||||
| 	} | ||||
| 	result = uwb_rc_mac_addr_setup(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot setup UWB MAC address: %d\n", result); | ||||
| 		goto error; | ||||
| 	} | ||||
| 	result = uwb_rc_dev_addr_assign(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot assign UWB DevAddr: %d\n", result); | ||||
| 		goto error; | ||||
| 	} | ||||
| 	result = uwb_rc_ie_setup(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot setup IE subsystem: %d\n", result); | ||||
| 		goto error_ie_setup; | ||||
| 	} | ||||
| 	result = uwb_rc_set_identification_ie(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot set Identification IE: %d\n", | ||||
| 			result); | ||||
| 		goto error_set_id_ie; | ||||
| 	} | ||||
| 	result = uwb_rsv_setup(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot setup reservation subsystem: %d\n", result); | ||||
| 		goto error_rsv_setup; | ||||
| 	} | ||||
| 	uwb_dbg_add_rc(rc); | ||||
| 	return 0; | ||||
| 
 | ||||
| error_rsv_setup: | ||||
| 	uwb_rc_ie_release(rc); | ||||
| error_ie_setup: | ||||
| error: | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Register a new UWB radio controller | ||||
|  * | ||||
|  * Did you call uwb_rc_init() on your rc? | ||||
|  * | ||||
|  * We assume that this is being called with a > 0 refcount on | ||||
|  * it [through ops->{get|put}_device(). We'll take our own, though. | ||||
|  * | ||||
|  * @parent_dev is our real device, the one that provides the actual UWB device | ||||
|  */ | ||||
| int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv) | ||||
| { | ||||
| 	int result; | ||||
| 	struct device *dev; | ||||
| 	char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE]; | ||||
| 
 | ||||
| 	rc->index = uwb_rc_new_index(); | ||||
| 
 | ||||
| 	dev = &rc->uwb_dev.dev; | ||||
| 	dev_set_name(dev, "uwb%d", rc->index); | ||||
| 
 | ||||
| 	rc->priv = priv; | ||||
| 
 | ||||
| 	result = rc->start(rc); | ||||
| 	if (result < 0) | ||||
| 		goto error_rc_start; | ||||
| 
 | ||||
| 	result = uwb_rc_setup(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(dev, "cannot setup UWB radio controller: %d\n", result); | ||||
| 		goto error_rc_setup; | ||||
| 	} | ||||
| 
 | ||||
| 	result = uwb_dev_add(&rc->uwb_dev, parent_dev, rc); | ||||
| 	if (result < 0 && result != -EADDRNOTAVAIL) | ||||
| 		goto error_dev_add; | ||||
| 
 | ||||
| 	result = uwb_rc_sys_add(rc); | ||||
| 	if (result < 0) { | ||||
| 		dev_err(parent_dev, "cannot register UWB radio controller " | ||||
| 			"dev attributes: %d\n", result); | ||||
| 		goto error_sys_add; | ||||
| 	} | ||||
| 
 | ||||
| 	uwb_mac_addr_print(macbuf, sizeof(macbuf), &rc->uwb_dev.mac_addr); | ||||
| 	uwb_dev_addr_print(devbuf, sizeof(devbuf), &rc->uwb_dev.dev_addr); | ||||
| 	dev_info(dev, | ||||
| 		 "new uwb radio controller (mac %s dev %s) on %s %s\n", | ||||
| 		 macbuf, devbuf, parent_dev->bus->name, dev_name(parent_dev)); | ||||
| 	rc->ready = 1; | ||||
| 	return 0; | ||||
| 
 | ||||
| error_sys_add: | ||||
| 	uwb_dev_rm(&rc->uwb_dev); | ||||
| error_dev_add: | ||||
| error_rc_setup: | ||||
| 	rc->stop(rc); | ||||
| 	uwbd_flush(rc); | ||||
| error_rc_start: | ||||
| 	return result; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_rc_add); | ||||
| 
 | ||||
| 
 | ||||
| static int uwb_dev_offair_helper(struct device *dev, void *priv) | ||||
| { | ||||
| 	struct uwb_dev *uwb_dev = to_uwb_dev(dev); | ||||
| 
 | ||||
| 	return __uwb_dev_offair(uwb_dev, uwb_dev->rc); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Remove a Radio Controller; stop beaconing/scanning, disconnect all children | ||||
|  */ | ||||
| void uwb_rc_rm(struct uwb_rc *rc) | ||||
| { | ||||
| 	rc->ready = 0; | ||||
| 
 | ||||
| 	uwb_dbg_del_rc(rc); | ||||
| 	uwb_rsv_cleanup(rc); | ||||
| 	uwb_rc_ie_rm(rc, UWB_IDENTIFICATION_IE); | ||||
| 	if (rc->beaconing >= 0) | ||||
| 		uwb_rc_beacon(rc, -1, 0); | ||||
| 	if (rc->scan_type != UWB_SCAN_DISABLED) | ||||
| 		uwb_rc_scan(rc, rc->scanning, UWB_SCAN_DISABLED, 0); | ||||
| 	uwb_rc_reset(rc); | ||||
| 
 | ||||
| 	rc->stop(rc); | ||||
| 	uwbd_flush(rc); | ||||
| 
 | ||||
| 	uwb_dev_lock(&rc->uwb_dev); | ||||
| 	rc->priv = NULL; | ||||
| 	rc->cmd = NULL; | ||||
| 	uwb_dev_unlock(&rc->uwb_dev); | ||||
| 	mutex_lock(&uwb_beca.mutex); | ||||
| 	uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL); | ||||
| 	__uwb_rc_sys_rm(rc); | ||||
| 	mutex_unlock(&uwb_beca.mutex); | ||||
| 	uwb_dev_rm(&rc->uwb_dev); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_rc_rm); | ||||
| 
 | ||||
| static int find_rc_try_get(struct device *dev, void *data) | ||||
| { | ||||
| 	struct uwb_rc *target_rc = data; | ||||
| 	struct uwb_rc *rc = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	if (rc == NULL) { | ||||
| 		WARN_ON(1); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (rc == target_rc) { | ||||
| 		if (rc->ready == 0) | ||||
| 			return 0; | ||||
| 		else | ||||
| 			return 1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Given a radio controller descriptor, validate and refcount it | ||||
|  * | ||||
|  * @returns NULL if the rc does not exist or is quiescing; the ptr to | ||||
|  *               it otherwise. | ||||
|  */ | ||||
| struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 	struct uwb_rc *rc = NULL; | ||||
| 
 | ||||
| 	dev = class_find_device(&uwb_rc_class, NULL, target_rc, | ||||
| 				find_rc_try_get); | ||||
| 	if (dev) { | ||||
| 		rc = dev_get_drvdata(dev); | ||||
| 		__uwb_rc_get(rc); | ||||
| 	} | ||||
| 	return rc; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(__uwb_rc_try_get); | ||||
| 
 | ||||
| /*
 | ||||
|  * RC get for external refcount acquirers... | ||||
|  * | ||||
|  * Increments the refcount of the device and it's backend modules | ||||
|  */ | ||||
| static inline struct uwb_rc *uwb_rc_get(struct uwb_rc *rc) | ||||
| { | ||||
| 	if (rc->ready == 0) | ||||
| 		return NULL; | ||||
| 	uwb_dev_get(&rc->uwb_dev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static int find_rc_grandpa(struct device *dev, void *data) | ||||
| { | ||||
| 	struct device *grandpa_dev = data; | ||||
| 	struct uwb_rc *rc = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	if (rc->uwb_dev.dev.parent->parent == grandpa_dev) { | ||||
| 		rc = uwb_rc_get(rc); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Locate and refcount a radio controller given a common grand-parent | ||||
|  * | ||||
|  * @grandpa_dev  Pointer to the 'grandparent' device structure. | ||||
|  * @returns NULL If the rc does not exist or is quiescing; the ptr to | ||||
|  *               it otherwise, properly referenced. | ||||
|  * | ||||
|  * The Radio Control interface (or the UWB Radio Controller) is always | ||||
|  * an interface of a device. The parent is the interface, the | ||||
|  * grandparent is the device that encapsulates the interface. | ||||
|  * | ||||
|  * There is no need to lock around as the "grandpa" would be | ||||
|  * refcounted by the target, and to remove the referemes, the | ||||
|  * uwb_rc_class->sem would have to be taken--we hold it, ergo we | ||||
|  * should be safe. | ||||
|  */ | ||||
| struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 	struct uwb_rc *rc = NULL; | ||||
| 
 | ||||
| 	dev = class_find_device(&uwb_rc_class, NULL, (void *)grandpa_dev, | ||||
| 				find_rc_grandpa); | ||||
| 	if (dev) | ||||
| 		rc = dev_get_drvdata(dev); | ||||
| 	return rc; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa); | ||||
| 
 | ||||
| /**
 | ||||
|  * Find a radio controller by device address | ||||
|  * | ||||
|  * @returns the pointer to the radio controller, properly referenced | ||||
|  */ | ||||
| static int find_rc_dev(struct device *dev, void *data) | ||||
| { | ||||
| 	struct uwb_dev_addr *addr = data; | ||||
| 	struct uwb_rc *rc = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	if (rc == NULL) { | ||||
| 		WARN_ON(1); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (!uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, addr)) { | ||||
| 		rc = uwb_rc_get(rc); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr) | ||||
| { | ||||
| 	struct device *dev; | ||||
| 	struct uwb_rc *rc = NULL; | ||||
| 
 | ||||
| 	dev = class_find_device(&uwb_rc_class, NULL, (void *)addr, | ||||
| 				find_rc_dev); | ||||
| 	if (dev) | ||||
| 		rc = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_rc_get_by_dev); | ||||
| 
 | ||||
| /**
 | ||||
|  * Drop a reference on a radio controller | ||||
|  * | ||||
|  * This is the version that should be done by entities external to the | ||||
|  * UWB Radio Control stack (ie: clients of the API). | ||||
|  */ | ||||
| void uwb_rc_put(struct uwb_rc *rc) | ||||
| { | ||||
| 	__uwb_rc_put(rc); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_rc_put); | ||||
| 
 | ||||
| /*
 | ||||
|  * | ||||
|  * | ||||
|  */ | ||||
| ssize_t uwb_rc_print_IEs(struct uwb_rc *uwb_rc, char *buf, size_t size) | ||||
| { | ||||
| 	ssize_t result; | ||||
| 	struct uwb_rc_evt_get_ie *ie_info; | ||||
| 	struct uwb_buf_ctx ctx; | ||||
| 
 | ||||
| 	result = uwb_rc_get_ie(uwb_rc, &ie_info); | ||||
| 	if (result < 0) | ||||
| 		goto error_get_ie; | ||||
| 	ctx.buf = buf; | ||||
| 	ctx.size = size; | ||||
| 	ctx.bytes = 0; | ||||
| 	uwb_ie_for_each(&uwb_rc->uwb_dev, uwb_ie_dump_hex, &ctx, | ||||
| 			ie_info->IEData, result - sizeof(*ie_info)); | ||||
| 	result = ctx.bytes; | ||||
| 	kfree(ie_info); | ||||
| error_get_ie: | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										71
									
								
								drivers/uwb/pal.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								drivers/uwb/pal.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | |||
| /*
 | ||||
|  * UWB PAL support. | ||||
|  * | ||||
|  * 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/kernel.h> | ||||
| #include <linux/uwb.h> | ||||
| 
 | ||||
| #include "uwb-internal.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * uwb_pal_init - initialize a UWB PAL | ||||
|  * @pal: the PAL to initialize | ||||
|  */ | ||||
| void uwb_pal_init(struct uwb_pal *pal) | ||||
| { | ||||
| 	INIT_LIST_HEAD(&pal->node); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_pal_init); | ||||
| 
 | ||||
| /**
 | ||||
|  * uwb_pal_register - register a UWB PAL | ||||
|  * @rc: the radio controller the PAL will be using | ||||
|  * @pal: the PAL | ||||
|  * | ||||
|  * The PAL must be initialized with uwb_pal_init(). | ||||
|  */ | ||||
| int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal) | ||||
| { | ||||
| 	spin_lock(&rc->pal_lock); | ||||
| 	list_add(&pal->node, &rc->pals); | ||||
| 	spin_unlock(&rc->pal_lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_pal_register); | ||||
| 
 | ||||
| /**
 | ||||
|  * uwb_pal_register - unregister a UWB PAL | ||||
|  * @rc: the radio controller the PAL was using | ||||
|  * @pal: the PAL | ||||
|  */ | ||||
| void uwb_pal_unregister(struct uwb_rc *rc, struct uwb_pal *pal) | ||||
| { | ||||
| 	spin_lock(&rc->pal_lock); | ||||
| 	list_del(&pal->node); | ||||
| 	spin_unlock(&rc->pal_lock); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(uwb_pal_unregister); | ||||
| 
 | ||||
| /**
 | ||||
|  * uwb_rc_pal_init - initialize the PAL related parts of a radio controller | ||||
|  * @rc: the radio controller | ||||
|  */ | ||||
| void uwb_rc_pal_init(struct uwb_rc *rc) | ||||
| { | ||||
| 	spin_lock_init(&rc->pal_lock); | ||||
| 	INIT_LIST_HEAD(&rc->pals); | ||||
| } | ||||
							
								
								
									
										306
									
								
								drivers/uwb/uwb-internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								drivers/uwb/uwb-internal.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,306 @@ | |||
| /*
 | ||||
|  * Ultra Wide Band | ||||
|  * UWB internal API | ||||
|  * | ||||
|  * Copyright (C) 2005-2006 Intel Corporation | ||||
|  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||||
|  * | ||||
|  * 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, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
|  * 02110-1301, USA. | ||||
|  * | ||||
|  * This contains most of the internal API for UWB. This is stuff used | ||||
|  * across the stack that of course, is of no interest to the rest. | ||||
|  * | ||||
|  * Some parts might end up going public (like uwb_rc_*())... | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __UWB_INTERNAL_H__ | ||||
| #define __UWB_INTERNAL_H__ | ||||
| 
 | ||||
| #include <linux/version.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/uwb.h> | ||||
| #include <linux/mutex.h> | ||||
| 
 | ||||
| struct uwb_beca_e; | ||||
| 
 | ||||
| /* General device API */ | ||||
| extern void uwb_dev_init(struct uwb_dev *uwb_dev); | ||||
| extern int __uwb_dev_offair(struct uwb_dev *, struct uwb_rc *); | ||||
| extern int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev, | ||||
| 		       struct uwb_rc *parent_rc); | ||||
| extern void uwb_dev_rm(struct uwb_dev *uwb_dev); | ||||
| extern void uwbd_dev_onair(struct uwb_rc *, struct uwb_beca_e *); | ||||
| extern void uwbd_dev_offair(struct uwb_beca_e *); | ||||
| void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event); | ||||
| 
 | ||||
| /* General UWB Radio Controller Internal API */ | ||||
| extern struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *); | ||||
| static inline struct uwb_rc *__uwb_rc_get(struct uwb_rc *rc) | ||||
| { | ||||
| 	uwb_dev_get(&rc->uwb_dev); | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| static inline void __uwb_rc_put(struct uwb_rc *rc) | ||||
| { | ||||
| 	uwb_dev_put(&rc->uwb_dev); | ||||
| } | ||||
| 
 | ||||
| extern int uwb_rc_reset(struct uwb_rc *rc); | ||||
| extern int uwb_rc_beacon(struct uwb_rc *rc, | ||||
| 			 int channel, unsigned bpst_offset); | ||||
| extern int uwb_rc_scan(struct uwb_rc *rc, | ||||
| 		       unsigned channel, enum uwb_scan_type type, | ||||
| 		       unsigned bpst_offset); | ||||
| extern int uwb_rc_send_all_drp_ie(struct uwb_rc *rc); | ||||
| extern ssize_t uwb_rc_print_IEs(struct uwb_rc *rc, char *, size_t); | ||||
| extern void uwb_rc_ie_init(struct uwb_rc *); | ||||
| extern void uwb_rc_ie_init(struct uwb_rc *); | ||||
| extern ssize_t uwb_rc_ie_setup(struct uwb_rc *); | ||||
| extern void uwb_rc_ie_release(struct uwb_rc *); | ||||
| extern int uwb_rc_ie_add(struct uwb_rc *, | ||||
| 			 const struct uwb_ie_hdr *, size_t); | ||||
| extern int uwb_rc_ie_rm(struct uwb_rc *, enum uwb_ie); | ||||
| extern int uwb_rc_set_identification_ie(struct uwb_rc *); | ||||
| 
 | ||||
| extern const char *uwb_rc_strerror(unsigned code); | ||||
| 
 | ||||
| /*
 | ||||
|  * Time to wait for a response to an RC command. | ||||
|  * | ||||
|  * Some commands can take a long time to response. e.g., START_BEACON | ||||
|  * may scan for several superframes before joining an existing beacon | ||||
|  * group and this can take around 600 ms. | ||||
|  */ | ||||
| #define UWB_RC_CMD_TIMEOUT_MS 1000 /* ms */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Notification/Event Handlers | ||||
|  */ | ||||
| 
 | ||||
| struct uwb_rc_neh; | ||||
| 
 | ||||
| void uwb_rc_neh_create(struct uwb_rc *rc); | ||||
| void uwb_rc_neh_destroy(struct uwb_rc *rc); | ||||
| 
 | ||||
| struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd, | ||||
| 				  u8 expected_type, u16 expected_event, | ||||
| 				  uwb_rc_cmd_cb_f cb, void *arg); | ||||
| void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh); | ||||
| void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh); | ||||
| void uwb_rc_neh_put(struct uwb_rc_neh *neh); | ||||
| 
 | ||||
| /* Event size tables */ | ||||
| extern int uwb_est_create(void); | ||||
| extern void uwb_est_destroy(void); | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * UWB Events & management daemon | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * enum uwb_event_type - types of UWB management daemon events | ||||
|  * | ||||
|  * The UWB management daemon (uwbd) can receive two types of events: | ||||
|  *   UWB_EVT_TYPE_NOTIF - notification from the radio controller. | ||||
|  *   UWB_EVT_TYPE_MSG   - a simple message. | ||||
|  */ | ||||
| enum uwb_event_type { | ||||
| 	UWB_EVT_TYPE_NOTIF, | ||||
| 	UWB_EVT_TYPE_MSG, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct uwb_event_notif - an event for a radio controller notification | ||||
|  * @size: Size of the buffer (ie: Guaranteed to contain at least | ||||
|  *        a full 'struct uwb_rceb') | ||||
|  * @rceb: Pointer to a kmalloced() event payload | ||||
|  */ | ||||
| struct uwb_event_notif { | ||||
| 	size_t size; | ||||
| 	struct uwb_rceb *rceb; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * enum uwb_event_message - an event for a message for asynchronous processing | ||||
|  * | ||||
|  * UWB_EVT_MSG_RESET - reset the radio controller and all PAL hardware. | ||||
|  */ | ||||
| enum uwb_event_message { | ||||
| 	UWB_EVT_MSG_RESET, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * UWB Event | ||||
|  * @rc:         Radio controller that emitted the event (referenced) | ||||
|  * @ts_jiffies: Timestamp, when was it received | ||||
|  * @type:       This event's type. | ||||
|  */ | ||||
| struct uwb_event { | ||||
| 	struct list_head list_node; | ||||
| 	struct uwb_rc *rc; | ||||
| 	unsigned long ts_jiffies; | ||||
| 	enum uwb_event_type type; | ||||
| 	union { | ||||
| 		struct uwb_event_notif notif; | ||||
| 		enum uwb_event_message message; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| extern void uwbd_start(void); | ||||
| extern void uwbd_stop(void); | ||||
| extern struct uwb_event *uwb_event_alloc(size_t, gfp_t gfp_mask); | ||||
| extern void uwbd_event_queue(struct uwb_event *); | ||||
| void uwbd_flush(struct uwb_rc *rc); | ||||
| 
 | ||||
| /* UWB event handlers */ | ||||
| extern int uwbd_evt_handle_rc_beacon(struct uwb_event *); | ||||
| extern int uwbd_evt_handle_rc_beacon_size(struct uwb_event *); | ||||
| extern int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *); | ||||
| extern int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *); | ||||
| extern int uwbd_evt_handle_rc_drp(struct uwb_event *); | ||||
| extern int uwbd_evt_handle_rc_drp_avail(struct uwb_event *); | ||||
| 
 | ||||
| int uwbd_msg_handle_reset(struct uwb_event *evt); | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Address management | ||||
|  */ | ||||
| int uwb_rc_dev_addr_assign(struct uwb_rc *rc); | ||||
| int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt); | ||||
| 
 | ||||
| /*
 | ||||
|  * UWB Beacon Cache | ||||
|  * | ||||
|  * Each beacon we received is kept in a cache--when we receive that | ||||
|  * beacon consistently, that means there is a new device that we have | ||||
|  * to add to the system. | ||||
|  */ | ||||
| 
 | ||||
| extern unsigned long beacon_timeout_ms; | ||||
| 
 | ||||
| /** Beacon cache list */ | ||||
| struct uwb_beca { | ||||
| 	struct list_head list; | ||||
| 	size_t entries; | ||||
| 	struct mutex mutex; | ||||
| }; | ||||
| 
 | ||||
| extern struct uwb_beca uwb_beca; | ||||
| 
 | ||||
| /**
 | ||||
|  * Beacon cache entry | ||||
|  * | ||||
|  * @jiffies_refresh: last time a beacon was  received that refreshed | ||||
|  *                   this cache entry. | ||||
|  * @uwb_dev: device connected to this beacon. This pointer is not | ||||
|  *           safe, you need to get it with uwb_dev_try_get() | ||||
|  * | ||||
|  * @hits: how many time we have seen this beacon since last time we | ||||
|  *        cleared it | ||||
|  */ | ||||
| struct uwb_beca_e { | ||||
| 	struct mutex mutex; | ||||
| 	struct kref refcnt; | ||||
| 	struct list_head node; | ||||
| 	struct uwb_mac_addr *mac_addr; | ||||
| 	struct uwb_dev_addr dev_addr; | ||||
| 	u8 hits; | ||||
| 	unsigned long ts_jiffies; | ||||
| 	struct uwb_dev *uwb_dev; | ||||
| 	struct uwb_rc_evt_beacon *be; | ||||
| 	struct stats lqe_stats, rssi_stats;	/* radio statistics */ | ||||
| }; | ||||
| struct uwb_beacon_frame; | ||||
| extern ssize_t uwb_bce_print_IEs(struct uwb_dev *, struct uwb_beca_e *, | ||||
| 				 char *, size_t); | ||||
| extern struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *, | ||||
| 					 struct uwb_beacon_frame *, | ||||
| 					 unsigned long); | ||||
| 
 | ||||
| extern void uwb_bce_kfree(struct kref *_bce); | ||||
| static inline void uwb_bce_get(struct uwb_beca_e *bce) | ||||
| { | ||||
| 	kref_get(&bce->refcnt); | ||||
| } | ||||
| static inline void uwb_bce_put(struct uwb_beca_e *bce) | ||||
| { | ||||
| 	kref_put(&bce->refcnt, uwb_bce_kfree); | ||||
| } | ||||
| extern void uwb_beca_purge(void); | ||||
| extern void uwb_beca_release(void); | ||||
| 
 | ||||
| struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc, | ||||
| 				       const struct uwb_dev_addr *devaddr); | ||||
| struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc, | ||||
| 				       const struct uwb_mac_addr *macaddr); | ||||
| 
 | ||||
| /* -- UWB Sysfs representation */ | ||||
| extern struct class uwb_rc_class; | ||||
| extern struct device_attribute dev_attr_mac_address; | ||||
| extern struct device_attribute dev_attr_beacon; | ||||
| extern struct device_attribute dev_attr_scan; | ||||
| 
 | ||||
| /* -- DRP Bandwidth allocator: bandwidth allocations, reservations, DRP */ | ||||
| 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_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_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); | ||||
| 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); | ||||
| void uwb_drp_avail_ie_update(struct uwb_rc *rc); | ||||
| 
 | ||||
| /* -- PAL support */ | ||||
| void uwb_rc_pal_init(struct uwb_rc *rc); | ||||
| 
 | ||||
| /* -- Misc */ | ||||
| 
 | ||||
| extern ssize_t uwb_mac_frame_hdr_print(char *, size_t, | ||||
| 				       const struct uwb_mac_frame_hdr *); | ||||
| 
 | ||||
| /* -- Debug interface */ | ||||
| void uwb_dbg_init(void); | ||||
| void uwb_dbg_exit(void); | ||||
| void uwb_dbg_add_rc(struct uwb_rc *rc); | ||||
| void uwb_dbg_del_rc(struct uwb_rc *rc); | ||||
| 
 | ||||
| /* Workarounds for version specific stuff */ | ||||
| 
 | ||||
| static inline void uwb_dev_lock(struct uwb_dev *uwb_dev) | ||||
| { | ||||
| 	down(&uwb_dev->dev.sem); | ||||
| } | ||||
| 
 | ||||
| static inline void uwb_dev_unlock(struct uwb_dev *uwb_dev) | ||||
| { | ||||
| 	up(&uwb_dev->dev.sem); | ||||
| } | ||||
| 
 | ||||
| #endif /* #ifndef __UWB_INTERNAL_H__ */ | ||||
							
								
								
									
										427
									
								
								drivers/uwb/uwbd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										427
									
								
								drivers/uwb/uwbd.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,427 @@ | |||
| /*
 | ||||
|  * Ultra Wide Band | ||||
|  * Neighborhood Management Daemon | ||||
|  * | ||||
|  * Copyright (C) 2005-2006 Intel Corporation | ||||
|  * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||||
|  * | ||||
|  * 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, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
|  * 02110-1301, USA. | ||||
|  * | ||||
|  * | ||||
|  * This daemon takes care of maintaing information that describes the | ||||
|  * UWB neighborhood that the radios in this machine can see. It also | ||||
|  * keeps a tab of which devices are visible, makes sure each HC sits | ||||
|  * on a different channel to avoid interfering, etc. | ||||
|  * | ||||
|  * Different drivers (radio controller, device, any API in general) | ||||
|  * communicate with this daemon through an event queue. Daemon wakes | ||||
|  * up, takes a list of events and handles them one by one; handling | ||||
|  * function is extracted from a table based on the event's type and | ||||
|  * subtype. Events are freed only if the handling function says so. | ||||
|  * | ||||
|  *   . Lock protecting the event list has to be an spinlock and locked | ||||
|  *     with IRQSAVE because it might be called from an interrupt | ||||
|  *     context (ie: when events arrive and the notification drops | ||||
|  *     down from the ISR). | ||||
|  * | ||||
|  *   . UWB radio controller drivers queue events to the daemon using | ||||
|  *     uwbd_event_queue(). They just get the event, chew it to make it | ||||
|  *     look like UWBD likes it and pass it in a buffer allocated with | ||||
|  *     uwb_event_alloc(). | ||||
|  * | ||||
|  * EVENTS | ||||
|  * | ||||
|  * Events have a type, a subtype, a lenght, some other stuff and the | ||||
|  * data blob, which depends on the event. The header is 'struct | ||||
|  * uwb_event'; for payloads, see 'struct uwbd_evt_*'. | ||||
|  * | ||||
|  * EVENT HANDLER TABLES | ||||
|  * | ||||
|  * To find a handling function for an event, the type is used to index | ||||
|  * a subtype-table in the type-table. The subtype-table is indexed | ||||
|  * with the subtype to get the function that handles the event. Start | ||||
|  * with the main type-table 'uwbd_evt_type_handler'. | ||||
|  * | ||||
|  * DEVICES | ||||
|  * | ||||
|  * Devices are created when a bunch of beacons have been received and | ||||
|  * it is stablished that the device has stable radio presence. CREATED | ||||
|  * only, not configured. Devices are ONLY configured when an | ||||
|  * Application-Specific IE Probe is receieved, in which the device | ||||
|  * declares which Protocol ID it groks. Then the device is CONFIGURED | ||||
|  * (and the driver->probe() stuff of the device model is invoked). | ||||
|  * | ||||
|  * Devices are considered disconnected when a certain number of | ||||
|  * beacons are not received in an amount of time. | ||||
|  * | ||||
|  * Handler functions are called normally uwbd_evt_handle_*(). | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kthread.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/freezer.h> | ||||
| #include "uwb-internal.h" | ||||
| 
 | ||||
| #define D_LOCAL 1 | ||||
| #include <linux/uwb/debug.h> | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * UWBD Event handler function signature | ||||
|  * | ||||
|  * Return !0 if the event needs not to be freed (ie the handler | ||||
|  * takes/took care of it). 0 means the daemon code will free the | ||||
|  * event. | ||||
|  * | ||||
|  * @evt->rc is already referenced and guaranteed to exist. See | ||||
|  * uwb_evt_handle(). | ||||
|  */ | ||||
| typedef int (*uwbd_evt_handler_f)(struct uwb_event *); | ||||
| 
 | ||||
| /**
 | ||||
|  * Properties of a UWBD event | ||||
|  * | ||||
|  * @handler:    the function that will handle this event | ||||
|  * @name:       text name of event | ||||
|  */ | ||||
| struct uwbd_event { | ||||
| 	uwbd_evt_handler_f handler; | ||||
| 	const char *name; | ||||
| }; | ||||
| 
 | ||||
| /** Table of handlers for and properties of the UWBD Radio Control Events */ | ||||
| static | ||||
| struct uwbd_event uwbd_events[] = { | ||||
| 	[UWB_RC_EVT_BEACON] = { | ||||
| 		.handler = uwbd_evt_handle_rc_beacon, | ||||
| 		.name = "BEACON_RECEIVED" | ||||
| 	}, | ||||
| 	[UWB_RC_EVT_BEACON_SIZE] = { | ||||
| 		.handler = uwbd_evt_handle_rc_beacon_size, | ||||
| 		.name = "BEACON_SIZE_CHANGE" | ||||
| 	}, | ||||
| 	[UWB_RC_EVT_BPOIE_CHANGE] = { | ||||
| 		.handler = uwbd_evt_handle_rc_bpoie_change, | ||||
| 		.name = "BPOIE_CHANGE" | ||||
| 	}, | ||||
| 	[UWB_RC_EVT_BP_SLOT_CHANGE] = { | ||||
| 		.handler = uwbd_evt_handle_rc_bp_slot_change, | ||||
| 		.name = "BP_SLOT_CHANGE" | ||||
| 	}, | ||||
| 	[UWB_RC_EVT_DRP_AVAIL] = { | ||||
| 		.handler = uwbd_evt_handle_rc_drp_avail, | ||||
| 		.name = "DRP_AVAILABILITY_CHANGE" | ||||
| 	}, | ||||
| 	[UWB_RC_EVT_DRP] = { | ||||
| 		.handler = uwbd_evt_handle_rc_drp, | ||||
| 		.name = "DRP" | ||||
| 	}, | ||||
| 	[UWB_RC_EVT_DEV_ADDR_CONFLICT] = { | ||||
| 		.handler = uwbd_evt_handle_rc_dev_addr_conflict, | ||||
| 		.name = "DEV_ADDR_CONFLICT", | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| struct uwbd_evt_type_handler { | ||||
| 	const char *name; | ||||
| 	struct uwbd_event *uwbd_events; | ||||
| 	size_t size; | ||||
| }; | ||||
| 
 | ||||
| #define UWBD_EVT_TYPE_HANDLER(n,a) {		\ | ||||
| 	.name = (n),				\ | ||||
| 	.uwbd_events = (a),			\ | ||||
| 	.size = sizeof(a)/sizeof((a)[0])	\ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** Table of handlers for each UWBD Event type. */ | ||||
| static | ||||
| struct uwbd_evt_type_handler uwbd_evt_type_handlers[] = { | ||||
| 	[UWB_RC_CET_GENERAL] = UWBD_EVT_TYPE_HANDLER("RC", uwbd_events) | ||||
| }; | ||||
| 
 | ||||
| static const | ||||
| size_t uwbd_evt_type_handlers_len = | ||||
| 	sizeof(uwbd_evt_type_handlers) / sizeof(uwbd_evt_type_handlers[0]); | ||||
| 
 | ||||
| static const struct uwbd_event uwbd_message_handlers[] = { | ||||
| 	[UWB_EVT_MSG_RESET] = { | ||||
| 		.handler = uwbd_msg_handle_reset, | ||||
| 		.name = "reset", | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static DEFINE_MUTEX(uwbd_event_mutex); | ||||
| 
 | ||||
| /**
 | ||||
|  * Handle an URC event passed to the UWB Daemon | ||||
|  * | ||||
|  * @evt: the event to handle | ||||
|  * @returns: 0 if the event can be kfreed, !0 on the contrary | ||||
|  *           (somebody else took ownership) [coincidentally, returning | ||||
|  *           a <0 errno code will free it :)]. | ||||
|  * | ||||
|  * Looks up the two indirection tables (one for the type, one for the | ||||
|  * subtype) to decide which function handles it and then calls the | ||||
|  * handler. | ||||
|  * | ||||
|  * The event structure passed to the event handler has the radio | ||||
|  * controller in @evt->rc referenced. The reference will be dropped | ||||
|  * once the handler returns, so if it needs it for longer (async), | ||||
|  * it'll need to take another one. | ||||
|  */ | ||||
| static | ||||
| int uwbd_event_handle_urc(struct uwb_event *evt) | ||||
| { | ||||
| 	int result; | ||||
| 	struct uwbd_evt_type_handler *type_table; | ||||
| 	uwbd_evt_handler_f handler; | ||||
| 	u8 type, context; | ||||
| 	u16 event; | ||||
| 
 | ||||
| 	type = evt->notif.rceb->bEventType; | ||||
| 	event = le16_to_cpu(evt->notif.rceb->wEvent); | ||||
| 	context = evt->notif.rceb->bEventContext; | ||||
| 
 | ||||
| 	if (type > uwbd_evt_type_handlers_len) { | ||||
| 		if (printk_ratelimit()) | ||||
| 			printk(KERN_ERR "UWBD: event type %u: unknown " | ||||
| 			       "(too high)\n", type); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	type_table = &uwbd_evt_type_handlers[type]; | ||||
| 	if (type_table->uwbd_events == NULL) { | ||||
| 		if (printk_ratelimit()) | ||||
| 			printk(KERN_ERR "UWBD: event type %u: unknown\n", type); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (event > type_table->size) { | ||||
| 		if (printk_ratelimit()) | ||||
| 			printk(KERN_ERR "UWBD: event %s[%u]: " | ||||
| 			       "unknown (too high)\n", type_table->name, event); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	handler = type_table->uwbd_events[event].handler; | ||||
| 	if (handler == NULL) { | ||||
| 		if (printk_ratelimit()) | ||||
| 			printk(KERN_ERR "UWBD: event %s[%u]: unknown\n", | ||||
| 			       type_table->name, event); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	d_printf(3, NULL, "processing 0x%02x/%04x/%02x, %zu bytes\n", | ||||
| 		 type, event, context, evt->notif.size); | ||||
| 	result = (*handler)(evt); | ||||
| 	if (result < 0) { | ||||
| 		if (printk_ratelimit()) | ||||
| 			printk(KERN_ERR "UWBD: event 0x%02x/%04x/%02x, " | ||||
| 			       "table %s[%u]: handling failed: %d\n", | ||||
| 			       type, event, context, type_table->name, | ||||
| 			       event, result); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| static void uwbd_event_handle_message(struct uwb_event *evt) | ||||
| { | ||||
| 	struct uwb_rc *rc; | ||||
| 	int result; | ||||
| 
 | ||||
| 	rc = evt->rc; | ||||
| 
 | ||||
| 	if (evt->message < 0 || evt->message >= ARRAY_SIZE(uwbd_message_handlers)) { | ||||
| 		dev_err(&rc->uwb_dev.dev, "UWBD: invalid message type %d\n", evt->message); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	/* If this is a reset event we need to drop the
 | ||||
| 	 * uwbd_event_mutex or it deadlocks when the reset handler | ||||
| 	 * attempts to flush the uwbd events. */ | ||||
| 	if (evt->message == UWB_EVT_MSG_RESET) | ||||
| 		mutex_unlock(&uwbd_event_mutex); | ||||
| 
 | ||||
| 	result = uwbd_message_handlers[evt->message].handler(evt); | ||||
| 	if (result < 0) | ||||
| 		dev_err(&rc->uwb_dev.dev, "UWBD: '%s' message failed: %d\n", | ||||
| 			uwbd_message_handlers[evt->message].name, result); | ||||
| 
 | ||||
| 	if (evt->message == UWB_EVT_MSG_RESET) | ||||
| 		mutex_lock(&uwbd_event_mutex); | ||||
| } | ||||
| 
 | ||||
| static void uwbd_event_handle(struct uwb_event *evt) | ||||
| { | ||||
| 	struct uwb_rc *rc; | ||||
| 	int should_keep; | ||||
| 
 | ||||
| 	rc = evt->rc; | ||||
| 
 | ||||
| 	if (rc->ready) { | ||||
| 		switch (evt->type) { | ||||
| 		case UWB_EVT_TYPE_NOTIF: | ||||
| 			should_keep = uwbd_event_handle_urc(evt); | ||||
| 			if (should_keep <= 0) | ||||
| 				kfree(evt->notif.rceb); | ||||
| 			break; | ||||
| 		case UWB_EVT_TYPE_MSG: | ||||
| 			uwbd_event_handle_message(evt); | ||||
| 			break; | ||||
| 		default: | ||||
| 			dev_err(&rc->uwb_dev.dev, "UWBD: invalid event type %d\n", evt->type); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	__uwb_rc_put(rc);	/* for the __uwb_rc_get() in uwb_rc_notif_cb() */ | ||||
| } | ||||
| /* The UWB Daemon */ | ||||
| 
 | ||||
| 
 | ||||
| /** Daemon's PID: used to decide if we can queue or not */ | ||||
| static int uwbd_pid; | ||||
| /** Daemon's task struct for managing the kthread */ | ||||
| static struct task_struct *uwbd_task; | ||||
| /** Daemon's waitqueue for waiting for new events */ | ||||
| static DECLARE_WAIT_QUEUE_HEAD(uwbd_wq); | ||||
| /** Daemon's list of events; we queue/dequeue here */ | ||||
| static struct list_head uwbd_event_list = LIST_HEAD_INIT(uwbd_event_list); | ||||
| /** Daemon's list lock to protect concurent access */ | ||||
| static DEFINE_SPINLOCK(uwbd_event_list_lock); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * UWB Daemon | ||||
|  * | ||||
|  * Listens to all UWB notifications and takes care to track the state | ||||
|  * of the UWB neighboorhood for the kernel. When we do a run, we | ||||
|  * spinlock, move the list to a private copy and release the | ||||
|  * lock. Hold it as little as possible. Not a conflict: it is | ||||
|  * guaranteed we own the events in the private list. | ||||
|  * | ||||
|  * FIXME: should change so we don't have a 1HZ timer all the time, but | ||||
|  *        only if there are devices. | ||||
|  */ | ||||
| static int uwbd(void *unused) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct list_head list = LIST_HEAD_INIT(list); | ||||
| 	struct uwb_event *evt, *nxt; | ||||
| 	int should_stop = 0; | ||||
| 	while (1) { | ||||
| 		wait_event_interruptible_timeout( | ||||
| 			uwbd_wq, | ||||
| 			!list_empty(&uwbd_event_list) | ||||
| 			  || (should_stop = kthread_should_stop()), | ||||
| 			HZ); | ||||
| 		if (should_stop) | ||||
| 			break; | ||||
| 		try_to_freeze(); | ||||
| 
 | ||||
| 		mutex_lock(&uwbd_event_mutex); | ||||
| 		spin_lock_irqsave(&uwbd_event_list_lock, flags); | ||||
| 		list_splice_init(&uwbd_event_list, &list); | ||||
| 		spin_unlock_irqrestore(&uwbd_event_list_lock, flags); | ||||
| 		list_for_each_entry_safe(evt, nxt, &list, list_node) { | ||||
| 			list_del(&evt->list_node); | ||||
| 			uwbd_event_handle(evt); | ||||
| 			kfree(evt); | ||||
| 		} | ||||
| 		mutex_unlock(&uwbd_event_mutex); | ||||
| 
 | ||||
| 		uwb_beca_purge();	/* Purge devices that left */ | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /** Start the UWB daemon */ | ||||
| void uwbd_start(void) | ||||
| { | ||||
| 	uwbd_task = kthread_run(uwbd, NULL, "uwbd"); | ||||
| 	if (uwbd_task == NULL) | ||||
| 		printk(KERN_ERR "UWB: Cannot start management daemon; " | ||||
| 		       "UWB won't work\n"); | ||||
| 	else | ||||
| 		uwbd_pid = uwbd_task->pid; | ||||
| } | ||||
| 
 | ||||
| /* Stop the UWB daemon and free any unprocessed events */ | ||||
| void uwbd_stop(void) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct uwb_event *evt, *nxt; | ||||
| 	kthread_stop(uwbd_task); | ||||
| 	spin_lock_irqsave(&uwbd_event_list_lock, flags); | ||||
| 	uwbd_pid = 0; | ||||
| 	list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) { | ||||
| 		if (evt->type == UWB_EVT_TYPE_NOTIF) | ||||
| 			kfree(evt->notif.rceb); | ||||
| 		kfree(evt); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&uwbd_event_list_lock, flags); | ||||
| 	uwb_beca_release(); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Queue an event for the management daemon | ||||
|  * | ||||
|  * When some lower layer receives an event, it uses this function to | ||||
|  * push it forward to the UWB daemon. | ||||
|  * | ||||
|  * Once you pass the event, you don't own it any more, but the daemon | ||||
|  * does. It will uwb_event_free() it when done, so make sure you | ||||
|  * uwb_event_alloc()ed it or bad things will happen. | ||||
|  * | ||||
|  * If the daemon is not running, we just free the event. | ||||
|  */ | ||||
| void uwbd_event_queue(struct uwb_event *evt) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	spin_lock_irqsave(&uwbd_event_list_lock, flags); | ||||
| 	if (uwbd_pid != 0) { | ||||
| 		list_add(&evt->list_node, &uwbd_event_list); | ||||
| 		wake_up_all(&uwbd_wq); | ||||
| 	} else { | ||||
| 		__uwb_rc_put(evt->rc); | ||||
| 		if (evt->type == UWB_EVT_TYPE_NOTIF) | ||||
| 			kfree(evt->notif.rceb); | ||||
| 		kfree(evt); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&uwbd_event_list_lock, flags); | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| void uwbd_flush(struct uwb_rc *rc) | ||||
| { | ||||
| 	struct uwb_event *evt, *nxt; | ||||
| 
 | ||||
| 	mutex_lock(&uwbd_event_mutex); | ||||
| 
 | ||||
| 	spin_lock_irq(&uwbd_event_list_lock); | ||||
| 	list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) { | ||||
| 		if (evt->rc == rc) { | ||||
| 			__uwb_rc_put(rc); | ||||
| 			list_del(&evt->list_node); | ||||
| 			if (evt->type == UWB_EVT_TYPE_NOTIF) | ||||
| 				kfree(evt->notif.rceb); | ||||
| 			kfree(evt); | ||||
| 		} | ||||
| 	} | ||||
| 	spin_unlock_irq(&uwbd_event_list_lock); | ||||
| 
 | ||||
| 	mutex_unlock(&uwbd_event_mutex); | ||||
| } | ||||
		Loading…
	
		Reference in a new issue
	
	 Inaky Perez-Gonzalez
						Inaky Perez-Gonzalez