| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * WiMedia Logical Link Control Protocol (WLP) | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2007 Intel Corporation | 
|  | 5 | * Reinette Chatre <reinette.chatre@intel.com> | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or | 
|  | 8 | * modify it under the terms of the GNU General Public License version | 
|  | 9 | * 2 as published by the Free Software Foundation. | 
|  | 10 | * | 
|  | 11 | * This program is distributed in the hope that it will be useful, | 
|  | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 14 | * GNU General Public License for more details. | 
|  | 15 | * | 
|  | 16 | * You should have received a copy of the GNU General Public License | 
|  | 17 | * along with this program; if not, write to the Free Software | 
|  | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 
|  | 19 | * 02110-1301, USA. | 
|  | 20 | * | 
|  | 21 | * | 
|  | 22 | * Implementation of the WLP association protocol. | 
|  | 23 | * | 
|  | 24 | * FIXME: Docs | 
|  | 25 | * | 
|  | 26 | * A UWB network interface will configure a WSS through wlp_wss_setup() after | 
|  | 27 | * the interface has been assigned a MAC address, typically after | 
|  | 28 | * "ifconfig" has been called. When the interface goes down it should call | 
|  | 29 | * wlp_wss_remove(). | 
|  | 30 | * | 
|  | 31 | * When the WSS is ready for use the user interacts via sysfs to create, | 
|  | 32 | * discover, and activate WSS. | 
|  | 33 | * | 
|  | 34 | * wlp_wss_enroll_activate() | 
|  | 35 | * | 
|  | 36 | * wlp_wss_create_activate() | 
|  | 37 | * 	wlp_wss_set_wssid_hash() | 
|  | 38 | * 		wlp_wss_comp_wssid_hash() | 
|  | 39 | * 	wlp_wss_sel_bcast_addr() | 
|  | 40 | * 	wlp_wss_sysfs_add() | 
|  | 41 | * | 
|  | 42 | * Called when no more references to WSS exist: | 
|  | 43 | * 	wlp_wss_release() | 
|  | 44 | * 		wlp_wss_reset() | 
|  | 45 | */ | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 46 | #include <linux/etherdevice.h> /* for is_valid_ether_addr */ | 
|  | 47 | #include <linux/skbuff.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 48 | #include <linux/slab.h> | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 49 | #include <linux/wlp.h> | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 50 |  | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 51 | #include "wlp-internal.h" | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 52 |  | 
|  | 53 | size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key) | 
|  | 54 | { | 
|  | 55 | size_t result; | 
|  | 56 |  | 
|  | 57 | result = scnprintf(buf, bufsize, | 
|  | 58 | "%02x %02x %02x %02x %02x %02x " | 
|  | 59 | "%02x %02x %02x %02x %02x %02x " | 
|  | 60 | "%02x %02x %02x %02x", | 
|  | 61 | key[0], key[1], key[2], key[3], | 
|  | 62 | key[4], key[5], key[6], key[7], | 
|  | 63 | key[8], key[9], key[10], key[11], | 
|  | 64 | key[12], key[13], key[14], key[15]); | 
|  | 65 | return result; | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | /** | 
|  | 69 | * Compute WSSID hash | 
|  | 70 | * WLP Draft 0.99 [7.2.1] | 
|  | 71 | * | 
|  | 72 | * The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR | 
|  | 73 | * of all octets in the WSSID. | 
|  | 74 | */ | 
|  | 75 | static | 
|  | 76 | u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid) | 
|  | 77 | { | 
|  | 78 | return wssid->data[0]  ^ wssid->data[1]  ^ wssid->data[2] | 
|  | 79 | ^ wssid->data[3]  ^ wssid->data[4]  ^ wssid->data[5] | 
|  | 80 | ^ wssid->data[6]  ^ wssid->data[7]  ^ wssid->data[8] | 
|  | 81 | ^ wssid->data[9]  ^ wssid->data[10] ^ wssid->data[11] | 
|  | 82 | ^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14] | 
|  | 83 | ^ wssid->data[15]; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | /** | 
|  | 87 | * Select a multicast EUI-48 for the WSS broadcast address. | 
|  | 88 | * WLP Draft 0.99 [7.2.1] | 
|  | 89 | * | 
|  | 90 | * Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP | 
|  | 91 | * range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive. | 
|  | 92 | * | 
|  | 93 | * This address is currently hardcoded. | 
|  | 94 | * FIXME? | 
|  | 95 | */ | 
|  | 96 | static | 
|  | 97 | struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss) | 
|  | 98 | { | 
|  | 99 | struct uwb_mac_addr bcast = { | 
|  | 100 | .data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 } | 
|  | 101 | }; | 
|  | 102 | return bcast; | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 | /** | 
|  | 106 | * Clear the contents of the WSS structure - all except kobj, mutex, virtual | 
|  | 107 | * | 
|  | 108 | * We do not want to reinitialize - the internal kobj should not change as | 
|  | 109 | * it still points to the parent received during setup. The mutex should | 
|  | 110 | * remain also. We thus just reset values individually. | 
|  | 111 | * The virutal address assigned to WSS will remain the same for the | 
|  | 112 | * lifetime of the WSS. We only reset the fields that can change during its | 
|  | 113 | * lifetime. | 
|  | 114 | */ | 
|  | 115 | void wlp_wss_reset(struct wlp_wss *wss) | 
|  | 116 | { | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 117 | memset(&wss->wssid, 0, sizeof(wss->wssid)); | 
|  | 118 | wss->hash = 0; | 
|  | 119 | memset(&wss->name[0], 0, sizeof(wss->name)); | 
|  | 120 | memset(&wss->bcast, 0, sizeof(wss->bcast)); | 
|  | 121 | wss->secure_status = WLP_WSS_UNSECURE; | 
|  | 122 | memset(&wss->master_key[0], 0, sizeof(wss->master_key)); | 
|  | 123 | wss->tag = 0; | 
|  | 124 | wss->state = WLP_WSS_STATE_NONE; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 125 | } | 
|  | 126 |  | 
|  | 127 | /** | 
|  | 128 | * Create sysfs infrastructure for WSS | 
|  | 129 | * | 
|  | 130 | * The WSS is configured to have the interface as parent (see wlp_wss_setup()) | 
|  | 131 | * a new sysfs directory that includes wssid as its name is created in the | 
|  | 132 | * interface's sysfs directory. The group of files interacting with WSS are | 
|  | 133 | * created also. | 
|  | 134 | */ | 
|  | 135 | static | 
|  | 136 | int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str) | 
|  | 137 | { | 
|  | 138 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 139 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 140 | int result; | 
|  | 141 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 142 | result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str); | 
|  | 143 | if (result < 0) | 
|  | 144 | return result; | 
|  | 145 | wss->kobj.ktype = &wss_ktype; | 
|  | 146 | result = kobject_init_and_add(&wss->kobj, | 
|  | 147 | &wss_ktype, wss->kobj.parent, "wlp"); | 
|  | 148 | if (result < 0) { | 
|  | 149 | dev_err(dev, "WLP: Cannot register WSS kobject.\n"); | 
|  | 150 | goto error_kobject_register; | 
|  | 151 | } | 
|  | 152 | result = sysfs_create_group(&wss->kobj, &wss_attr_group); | 
|  | 153 | if (result < 0) { | 
|  | 154 | dev_err(dev, "WLP: Cannot register WSS attributes: %d\n", | 
|  | 155 | result); | 
|  | 156 | goto error_sysfs_create_group; | 
|  | 157 | } | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 158 | return 0; | 
|  | 159 | error_sysfs_create_group: | 
|  | 160 |  | 
|  | 161 | kobject_put(&wss->kobj); /* will free name if needed */ | 
|  | 162 | return result; | 
|  | 163 | error_kobject_register: | 
|  | 164 | kfree(wss->kobj.name); | 
|  | 165 | wss->kobj.name = NULL; | 
|  | 166 | wss->kobj.ktype = NULL; | 
|  | 167 | return result; | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 |  | 
|  | 171 | /** | 
|  | 172 | * Release WSS | 
|  | 173 | * | 
|  | 174 | * No more references exist to this WSS. We should undo everything that was | 
|  | 175 | * done in wlp_wss_create_activate() except removing the group. The group | 
|  | 176 | * is not removed because an object can be unregistered before the group is | 
|  | 177 | * created. We also undo any additional operations on the WSS after this | 
|  | 178 | * (addition of members). | 
|  | 179 | * | 
|  | 180 | * If memory was allocated for the kobject's name then it will | 
|  | 181 | * be freed by the kobject system during this time. | 
|  | 182 | * | 
| Uwe Kleine-König | 421f91d | 2010-06-11 12:17:00 +0200 | [diff] [blame] | 183 | * The EDA cache is removed and reinitialized when the WSS is removed. We | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 184 | * thus loose knowledge of members of this WSS at that time and need not do | 
|  | 185 | * it here. | 
|  | 186 | */ | 
|  | 187 | void wlp_wss_release(struct kobject *kobj) | 
|  | 188 | { | 
|  | 189 | struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj); | 
|  | 190 |  | 
|  | 191 | wlp_wss_reset(wss); | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | /** | 
|  | 195 | * Enroll into a WSS using provided neighbor as registrar | 
|  | 196 | * | 
|  | 197 | * First search the neighborhood information to learn which neighbor is | 
|  | 198 | * referred to, next proceed with enrollment. | 
|  | 199 | * | 
|  | 200 | * &wss->mutex is held | 
|  | 201 | */ | 
|  | 202 | static | 
|  | 203 | int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid, | 
|  | 204 | struct uwb_dev_addr *dest) | 
|  | 205 | { | 
|  | 206 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 207 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 208 | struct wlp_neighbor_e *neighbor; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 209 | int result = -ENXIO; | 
|  | 210 | struct uwb_dev_addr *dev_addr; | 
|  | 211 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 212 | mutex_lock(&wlp->nbmutex); | 
|  | 213 | list_for_each_entry(neighbor, &wlp->neighbors, node) { | 
|  | 214 | dev_addr = &neighbor->uwb_dev->dev_addr; | 
|  | 215 | if (!memcmp(dest, dev_addr, sizeof(*dest))) { | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 216 | result = wlp_enroll_neighbor(wlp, neighbor, wss, wssid); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 217 | break; | 
|  | 218 | } | 
|  | 219 | } | 
|  | 220 | if (result == -ENXIO) | 
|  | 221 | dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n", | 
|  | 222 | dest->data[1], dest->data[0]); | 
|  | 223 | mutex_unlock(&wlp->nbmutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 224 | return result; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | /** | 
|  | 228 | * Enroll into a WSS previously discovered | 
|  | 229 | * | 
|  | 230 | * User provides WSSID of WSS, search for neighbor that has this WSS | 
|  | 231 | * activated and attempt to enroll. | 
|  | 232 | * | 
|  | 233 | * &wss->mutex is held | 
|  | 234 | */ | 
|  | 235 | static | 
|  | 236 | int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid) | 
|  | 237 | { | 
|  | 238 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 239 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 240 | struct wlp_neighbor_e *neighbor; | 
|  | 241 | struct wlp_wssid_e *wssid_e; | 
|  | 242 | char buf[WLP_WSS_UUID_STRSIZE]; | 
|  | 243 | int result = -ENXIO; | 
|  | 244 |  | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 245 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 246 | mutex_lock(&wlp->nbmutex); | 
|  | 247 | list_for_each_entry(neighbor, &wlp->neighbors, node) { | 
|  | 248 | list_for_each_entry(wssid_e, &neighbor->wssid, node) { | 
|  | 249 | if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) { | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 250 | result = wlp_enroll_neighbor(wlp, neighbor, | 
|  | 251 | wss, wssid); | 
|  | 252 | if (result == 0) /* enrollment success */ | 
|  | 253 | goto out; | 
|  | 254 | break; | 
|  | 255 | } | 
|  | 256 | } | 
|  | 257 | } | 
|  | 258 | out: | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 259 | if (result == -ENXIO) { | 
|  | 260 | wlp_wss_uuid_print(buf, sizeof(buf), wssid); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 261 | dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf); | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 262 | } | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 263 | mutex_unlock(&wlp->nbmutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 264 | return result; | 
|  | 265 | } | 
|  | 266 |  | 
|  | 267 | /** | 
|  | 268 | * Enroll into WSS with provided WSSID, registrar may be provided | 
|  | 269 | * | 
|  | 270 | * @wss: out WSS that will be enrolled | 
|  | 271 | * @wssid: wssid of neighboring WSS that we want to enroll in | 
|  | 272 | * @devaddr: registrar can be specified, will be broadcast (ff:ff) if any | 
|  | 273 | *           neighbor can be used as registrar. | 
|  | 274 | * | 
|  | 275 | * &wss->mutex is held | 
|  | 276 | */ | 
|  | 277 | static | 
|  | 278 | int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid, | 
|  | 279 | struct uwb_dev_addr *devaddr) | 
|  | 280 | { | 
|  | 281 | int result; | 
|  | 282 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 283 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 284 | char buf[WLP_WSS_UUID_STRSIZE]; | 
|  | 285 | struct uwb_dev_addr bcast = {.data = {0xff, 0xff} }; | 
|  | 286 |  | 
|  | 287 | wlp_wss_uuid_print(buf, sizeof(buf), wssid); | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 288 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 289 | if (wss->state != WLP_WSS_STATE_NONE) { | 
|  | 290 | dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf); | 
|  | 291 | result = -EEXIST; | 
|  | 292 | goto error; | 
|  | 293 | } | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 294 | if (!memcmp(&bcast, devaddr, sizeof(bcast))) | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 295 | result = wlp_wss_enroll_discovered(wss, wssid); | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 296 | else | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 297 | result = wlp_wss_enroll_target(wss, wssid, devaddr); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 298 | if (result < 0) { | 
|  | 299 | dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n", | 
|  | 300 | buf, result); | 
|  | 301 | goto error; | 
|  | 302 | } | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 303 | dev_dbg(dev, "Successfully enrolled into WSS %s \n", buf); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 304 | result = wlp_wss_sysfs_add(wss, buf); | 
|  | 305 | if (result < 0) { | 
|  | 306 | dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n"); | 
|  | 307 | wlp_wss_reset(wss); | 
|  | 308 | } | 
|  | 309 | error: | 
|  | 310 | return result; | 
|  | 311 |  | 
|  | 312 | } | 
|  | 313 |  | 
|  | 314 | /** | 
|  | 315 | * Activate given WSS | 
|  | 316 | * | 
|  | 317 | * Prior to activation a WSS must be enrolled. To activate a WSS a device | 
|  | 318 | * includes the WSS hash in the WLP IE in its beacon in each superframe. | 
|  | 319 | * WLP 0.99 [7.2.5]. | 
|  | 320 | * | 
|  | 321 | * The WSS tag is also computed at this time. We only support one activated | 
|  | 322 | * WSS so we can use the hash as a tag - there will never be a conflict. | 
|  | 323 | * | 
|  | 324 | * We currently only support one activated WSS so only one WSS hash is | 
|  | 325 | * included in the WLP IE. | 
|  | 326 | */ | 
|  | 327 | static | 
|  | 328 | int wlp_wss_activate(struct wlp_wss *wss) | 
|  | 329 | { | 
|  | 330 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 331 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 332 | struct uwb_rc *uwb_rc = wlp->rc; | 
|  | 333 | int result; | 
|  | 334 | struct { | 
|  | 335 | struct wlp_ie wlp_ie; | 
|  | 336 | u8 hash; /* only include one hash */ | 
|  | 337 | } ie_data; | 
|  | 338 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 339 | BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED); | 
|  | 340 | wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid); | 
|  | 341 | wss->tag = wss->hash; | 
|  | 342 | memset(&ie_data, 0, sizeof(ie_data)); | 
|  | 343 | ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP; | 
|  | 344 | ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr); | 
|  | 345 | wlp_ie_set_hash_length(&ie_data.wlp_ie, sizeof(ie_data.hash)); | 
|  | 346 | ie_data.hash = wss->hash; | 
|  | 347 | result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr, | 
|  | 348 | sizeof(ie_data)); | 
|  | 349 | if (result < 0) { | 
|  | 350 | dev_err(dev, "WLP: Unable to add WLP IE to beacon. " | 
|  | 351 | "result = %d.\n", result); | 
|  | 352 | goto error_wlp_ie; | 
|  | 353 | } | 
|  | 354 | wss->state = WLP_WSS_STATE_ACTIVE; | 
|  | 355 | result = 0; | 
|  | 356 | error_wlp_ie: | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 357 | return result; | 
|  | 358 | } | 
|  | 359 |  | 
|  | 360 | /** | 
|  | 361 | * Enroll in and activate WSS identified by provided WSSID | 
|  | 362 | * | 
|  | 363 | * The neighborhood cache should contain a list of all neighbors and the | 
|  | 364 | * WSS they have activated. Based on that cache we search which neighbor we | 
|  | 365 | * can perform the association process with. The user also has option to | 
|  | 366 | * specify which neighbor it prefers as registrar. | 
|  | 367 | * Successful enrollment is followed by activation. | 
|  | 368 | * Successful activation will create the sysfs directory containing | 
|  | 369 | * specific information regarding this WSS. | 
|  | 370 | */ | 
|  | 371 | int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, | 
|  | 372 | struct uwb_dev_addr *devaddr) | 
|  | 373 | { | 
|  | 374 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 375 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 376 | int result = 0; | 
|  | 377 | char buf[WLP_WSS_UUID_STRSIZE]; | 
|  | 378 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 379 | mutex_lock(&wss->mutex); | 
|  | 380 | result = wlp_wss_enroll(wss, wssid, devaddr); | 
|  | 381 | if (result < 0) { | 
|  | 382 | wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid); | 
|  | 383 | dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf); | 
|  | 384 | goto error_enroll; | 
|  | 385 | } | 
|  | 386 | result = wlp_wss_activate(wss); | 
|  | 387 | if (result < 0) { | 
|  | 388 | dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment " | 
|  | 389 | "result = %d \n", result); | 
|  | 390 | /* Undo enrollment */ | 
|  | 391 | wlp_wss_reset(wss); | 
|  | 392 | goto error_activate; | 
|  | 393 | } | 
|  | 394 | error_activate: | 
|  | 395 | error_enroll: | 
|  | 396 | mutex_unlock(&wss->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 397 | return result; | 
|  | 398 | } | 
|  | 399 |  | 
|  | 400 | /** | 
|  | 401 | * Create, enroll, and activate a new WSS | 
|  | 402 | * | 
|  | 403 | * @wssid: new wssid provided by user | 
|  | 404 | * @name:  WSS name requested by used. | 
|  | 405 | * @sec_status: security status requested by user | 
|  | 406 | * | 
|  | 407 | * A user requested the creation of a new WSS. All operations are done | 
|  | 408 | * locally. The new WSS will be stored locally, the hash will be included | 
|  | 409 | * in the WLP IE, and the sysfs infrastructure for this WSS will be | 
|  | 410 | * created. | 
|  | 411 | */ | 
|  | 412 | int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid, | 
|  | 413 | char *name, unsigned sec_status, unsigned accept) | 
|  | 414 | { | 
|  | 415 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 416 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 417 | int result = 0; | 
|  | 418 | char buf[WLP_WSS_UUID_STRSIZE]; | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 419 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 420 | result = wlp_wss_uuid_print(buf, sizeof(buf), wssid); | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 421 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 422 | if (!mutex_trylock(&wss->mutex)) { | 
|  | 423 | dev_err(dev, "WLP: WLP association session in progress.\n"); | 
|  | 424 | return -EBUSY; | 
|  | 425 | } | 
|  | 426 | if (wss->state != WLP_WSS_STATE_NONE) { | 
|  | 427 | dev_err(dev, "WLP: WSS already exists. Not creating new.\n"); | 
|  | 428 | result = -EEXIST; | 
|  | 429 | goto out; | 
|  | 430 | } | 
|  | 431 | if (wss->kobj.parent == NULL) { | 
|  | 432 | dev_err(dev, "WLP: WSS parent not ready. Is network interface " | 
|  | 433 | "up?\n"); | 
|  | 434 | result = -ENXIO; | 
|  | 435 | goto out; | 
|  | 436 | } | 
|  | 437 | if (sec_status == WLP_WSS_SECURE) { | 
|  | 438 | dev_err(dev, "WLP: FIXME Creation of secure WSS not " | 
|  | 439 | "supported yet.\n"); | 
|  | 440 | result = -EINVAL; | 
|  | 441 | goto out; | 
|  | 442 | } | 
|  | 443 | wss->wssid = *wssid; | 
|  | 444 | memcpy(wss->name, name, sizeof(wss->name)); | 
|  | 445 | wss->bcast = wlp_wss_sel_bcast_addr(wss); | 
|  | 446 | wss->secure_status = sec_status; | 
|  | 447 | wss->accept_enroll = accept; | 
|  | 448 | /*wss->virtual_addr is initialized in call to wlp_wss_setup*/ | 
|  | 449 | /* sysfs infrastructure */ | 
|  | 450 | result = wlp_wss_sysfs_add(wss, buf); | 
|  | 451 | if (result < 0) { | 
|  | 452 | dev_err(dev, "Cannot set up sysfs for WSS kobject.\n"); | 
|  | 453 | wlp_wss_reset(wss); | 
|  | 454 | goto out; | 
|  | 455 | } else | 
|  | 456 | result = 0; | 
|  | 457 | wss->state = WLP_WSS_STATE_ENROLLED; | 
|  | 458 | result = wlp_wss_activate(wss); | 
|  | 459 | if (result < 0) { | 
|  | 460 | dev_err(dev, "WLP: Unable to activate WSS. Undoing " | 
|  | 461 | "enrollment\n"); | 
|  | 462 | wlp_wss_reset(wss); | 
|  | 463 | goto out; | 
|  | 464 | } | 
|  | 465 | result = 0; | 
|  | 466 | out: | 
|  | 467 | mutex_unlock(&wss->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 468 | return result; | 
|  | 469 | } | 
|  | 470 |  | 
|  | 471 | /** | 
|  | 472 | * Determine if neighbor has WSS activated | 
|  | 473 | * | 
|  | 474 | * @returns: 1 if neighbor has WSS activated, zero otherwise | 
|  | 475 | * | 
|  | 476 | * This can be done in two ways: | 
|  | 477 | * - send a C1 frame, parse C2/F0 response | 
|  | 478 | * - examine the WLP IE sent by the neighbor | 
|  | 479 | * | 
|  | 480 | * The WLP IE is not fully supported in hardware so we use the C1/C2 frame | 
|  | 481 | * exchange to determine if a WSS is activated. Using the WLP IE should be | 
|  | 482 | * faster and should be used when it becomes possible. | 
|  | 483 | */ | 
|  | 484 | int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss, | 
|  | 485 | struct uwb_dev_addr *dev_addr) | 
|  | 486 | { | 
|  | 487 | int result = 0; | 
|  | 488 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 489 | DECLARE_COMPLETION_ONSTACK(completion); | 
|  | 490 | struct wlp_session session; | 
|  | 491 | struct sk_buff  *skb; | 
|  | 492 | struct wlp_frame_assoc *resp; | 
|  | 493 | struct wlp_uuid wssid; | 
|  | 494 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 495 | mutex_lock(&wlp->mutex); | 
|  | 496 | /* Send C1 association frame */ | 
|  | 497 | result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1); | 
|  | 498 | if (result < 0) { | 
|  | 499 | dev_err(dev, "Unable to send C1 frame to neighbor " | 
|  | 500 | "%02x:%02x (%d)\n", dev_addr->data[1], | 
|  | 501 | dev_addr->data[0], result); | 
|  | 502 | result = 0; | 
|  | 503 | goto out; | 
|  | 504 | } | 
|  | 505 | /* Create session, wait for response */ | 
|  | 506 | session.exp_message = WLP_ASSOC_C2; | 
|  | 507 | session.cb = wlp_session_cb; | 
|  | 508 | session.cb_priv = &completion; | 
|  | 509 | session.neighbor_addr = *dev_addr; | 
|  | 510 | BUG_ON(wlp->session != NULL); | 
|  | 511 | wlp->session = &session; | 
|  | 512 | /* Wait for C2/F0 frame */ | 
|  | 513 | result = wait_for_completion_interruptible_timeout(&completion, | 
|  | 514 | WLP_PER_MSG_TIMEOUT * HZ); | 
|  | 515 | if (result == 0) { | 
|  | 516 | dev_err(dev, "Timeout while sending C1 to neighbor " | 
|  | 517 | "%02x:%02x.\n", dev_addr->data[1], | 
|  | 518 | dev_addr->data[0]); | 
|  | 519 | goto out; | 
|  | 520 | } | 
|  | 521 | if (result < 0) { | 
|  | 522 | dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n", | 
|  | 523 | dev_addr->data[1], dev_addr->data[0]); | 
|  | 524 | result = 0; | 
|  | 525 | goto out; | 
|  | 526 | } | 
|  | 527 | /* Parse message in session->data: it will be either C2 or F0 */ | 
|  | 528 | skb = session.data; | 
|  | 529 | resp = (void *) skb->data; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 530 | if (resp->type == WLP_ASSOC_F0) { | 
|  | 531 | result = wlp_parse_f0(wlp, skb); | 
|  | 532 | if (result < 0) | 
|  | 533 | dev_err(dev, "WLP:  unable to parse incoming F0 " | 
|  | 534 | "frame from neighbor %02x:%02x.\n", | 
|  | 535 | dev_addr->data[1], dev_addr->data[0]); | 
|  | 536 | result = 0; | 
|  | 537 | goto error_resp_parse; | 
|  | 538 | } | 
|  | 539 | /* WLP version and message type fields have already been parsed */ | 
|  | 540 | result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid, | 
|  | 541 | skb->len - sizeof(*resp)); | 
|  | 542 | if (result < 0) { | 
|  | 543 | dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n"); | 
|  | 544 | result = 0; | 
|  | 545 | goto error_resp_parse; | 
|  | 546 | } | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 547 | if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))) | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 548 | result = 1; | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 549 | else { | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 550 | dev_err(dev, "WLP: Received a C2 frame without matching " | 
|  | 551 | "WSSID.\n"); | 
|  | 552 | result = 0; | 
|  | 553 | } | 
|  | 554 | error_resp_parse: | 
|  | 555 | kfree_skb(skb); | 
|  | 556 | out: | 
|  | 557 | wlp->session = NULL; | 
|  | 558 | mutex_unlock(&wlp->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 559 | return result; | 
|  | 560 | } | 
|  | 561 |  | 
|  | 562 | /** | 
|  | 563 | * Activate connection with neighbor by updating EDA cache | 
|  | 564 | * | 
|  | 565 | * @wss:       local WSS to which neighbor wants to connect | 
|  | 566 | * @dev_addr:  neighbor's address | 
|  | 567 | * @wssid:     neighbor's WSSID - must be same as our WSS's WSSID | 
|  | 568 | * @tag:       neighbor's WSS tag used to identify frames transmitted by it | 
|  | 569 | * @virt_addr: neighbor's virtual EUI-48 | 
|  | 570 | */ | 
|  | 571 | static | 
|  | 572 | int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss, | 
|  | 573 | struct uwb_dev_addr *dev_addr, | 
|  | 574 | struct wlp_uuid *wssid, u8 *tag, | 
|  | 575 | struct uwb_mac_addr *virt_addr) | 
|  | 576 | { | 
|  | 577 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 578 | int result = 0; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 579 |  | 
|  | 580 | if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) { | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 581 | /* Update EDA cache */ | 
|  | 582 | result = wlp_eda_update_node(&wlp->eda, dev_addr, wss, | 
|  | 583 | (void *) virt_addr->data, *tag, | 
|  | 584 | WLP_WSS_CONNECTED); | 
|  | 585 | if (result < 0) | 
|  | 586 | dev_err(dev, "WLP: Unable to update EDA cache " | 
|  | 587 | "with new connected neighbor information.\n"); | 
|  | 588 | } else { | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 589 | dev_err(dev, "WLP: Neighbor does not have matching WSSID.\n"); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 590 | result = -EINVAL; | 
|  | 591 | } | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 592 | return result; | 
|  | 593 | } | 
|  | 594 |  | 
|  | 595 | /** | 
|  | 596 | * Connect to WSS neighbor | 
|  | 597 | * | 
|  | 598 | * Use C3/C4 exchange to determine if neighbor has WSS activated and | 
|  | 599 | * retrieve the WSS tag and virtual EUI-48 of the neighbor. | 
|  | 600 | */ | 
|  | 601 | static | 
|  | 602 | int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss, | 
|  | 603 | struct uwb_dev_addr *dev_addr) | 
|  | 604 | { | 
|  | 605 | int result; | 
|  | 606 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 607 | struct wlp_uuid wssid; | 
|  | 608 | u8 tag; | 
|  | 609 | struct uwb_mac_addr virt_addr; | 
|  | 610 | DECLARE_COMPLETION_ONSTACK(completion); | 
|  | 611 | struct wlp_session session; | 
|  | 612 | struct wlp_frame_assoc *resp; | 
|  | 613 | struct sk_buff *skb; | 
|  | 614 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 615 | mutex_lock(&wlp->mutex); | 
|  | 616 | /* Send C3 association frame */ | 
|  | 617 | result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3); | 
|  | 618 | if (result < 0) { | 
|  | 619 | dev_err(dev, "Unable to send C3 frame to neighbor " | 
|  | 620 | "%02x:%02x (%d)\n", dev_addr->data[1], | 
|  | 621 | dev_addr->data[0], result); | 
|  | 622 | goto out; | 
|  | 623 | } | 
|  | 624 | /* Create session, wait for response */ | 
|  | 625 | session.exp_message = WLP_ASSOC_C4; | 
|  | 626 | session.cb = wlp_session_cb; | 
|  | 627 | session.cb_priv = &completion; | 
|  | 628 | session.neighbor_addr = *dev_addr; | 
|  | 629 | BUG_ON(wlp->session != NULL); | 
|  | 630 | wlp->session = &session; | 
|  | 631 | /* Wait for C4/F0 frame */ | 
|  | 632 | result = wait_for_completion_interruptible_timeout(&completion, | 
|  | 633 | WLP_PER_MSG_TIMEOUT * HZ); | 
|  | 634 | if (result == 0) { | 
|  | 635 | dev_err(dev, "Timeout while sending C3 to neighbor " | 
|  | 636 | "%02x:%02x.\n", dev_addr->data[1], | 
|  | 637 | dev_addr->data[0]); | 
|  | 638 | result = -ETIMEDOUT; | 
|  | 639 | goto out; | 
|  | 640 | } | 
|  | 641 | if (result < 0) { | 
|  | 642 | dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n", | 
|  | 643 | dev_addr->data[1], dev_addr->data[0]); | 
|  | 644 | goto out; | 
|  | 645 | } | 
|  | 646 | /* Parse message in session->data: it will be either C4 or F0 */ | 
|  | 647 | skb = session.data; | 
|  | 648 | resp = (void *) skb->data; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 649 | if (resp->type == WLP_ASSOC_F0) { | 
|  | 650 | result = wlp_parse_f0(wlp, skb); | 
|  | 651 | if (result < 0) | 
|  | 652 | dev_err(dev, "WLP: unable to parse incoming F0 " | 
|  | 653 | "frame from neighbor %02x:%02x.\n", | 
|  | 654 | dev_addr->data[1], dev_addr->data[0]); | 
|  | 655 | result = -EINVAL; | 
|  | 656 | goto error_resp_parse; | 
|  | 657 | } | 
|  | 658 | result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr); | 
|  | 659 | if (result < 0) { | 
|  | 660 | dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n"); | 
|  | 661 | goto error_resp_parse; | 
|  | 662 | } | 
|  | 663 | result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag, | 
|  | 664 | &virt_addr); | 
|  | 665 | if (result < 0) { | 
|  | 666 | dev_err(dev, "WLP: Unable to activate connection to " | 
|  | 667 | "neighbor %02x:%02x.\n", dev_addr->data[1], | 
|  | 668 | dev_addr->data[0]); | 
|  | 669 | goto error_resp_parse; | 
|  | 670 | } | 
|  | 671 | error_resp_parse: | 
|  | 672 | kfree_skb(skb); | 
|  | 673 | out: | 
|  | 674 | /* Record that we unsuccessfully tried to connect to this neighbor */ | 
|  | 675 | if (result < 0) | 
|  | 676 | wlp_eda_update_node_state(&wlp->eda, dev_addr, | 
|  | 677 | WLP_WSS_CONNECT_FAILED); | 
|  | 678 | wlp->session = NULL; | 
|  | 679 | mutex_unlock(&wlp->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 680 | return result; | 
|  | 681 | } | 
|  | 682 |  | 
|  | 683 | /** | 
|  | 684 | * Connect to neighbor with common WSS, send pending frame | 
|  | 685 | * | 
|  | 686 | * This function is scheduled when a frame is destined to a neighbor with | 
|  | 687 | * which we do not have a connection. A copy of the EDA cache entry is | 
|  | 688 | * provided - not the actual cache entry (because it is protected by a | 
|  | 689 | * spinlock). | 
|  | 690 | * | 
|  | 691 | * First determine if neighbor has the same WSS activated, connect if it | 
|  | 692 | * does. The C3/C4 exchange is dual purpose to determine if neighbor has | 
|  | 693 | * WSS activated and proceed with the connection. | 
|  | 694 | * | 
|  | 695 | * The frame that triggered the connection setup is sent after connection | 
|  | 696 | * setup. | 
|  | 697 | * | 
|  | 698 | * network queue is stopped - we need to restart when done | 
|  | 699 | * | 
|  | 700 | */ | 
|  | 701 | static | 
|  | 702 | void wlp_wss_connect_send(struct work_struct *ws) | 
|  | 703 | { | 
|  | 704 | struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws, | 
|  | 705 | struct wlp_assoc_conn_ctx, | 
|  | 706 | ws); | 
|  | 707 | struct wlp *wlp = conn_ctx->wlp; | 
|  | 708 | struct sk_buff *skb = conn_ctx->skb; | 
|  | 709 | struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry; | 
|  | 710 | struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; | 
|  | 711 | struct wlp_wss *wss = &wlp->wss; | 
|  | 712 | int result; | 
|  | 713 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 714 |  | 
|  | 715 | mutex_lock(&wss->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 716 | if (wss->state < WLP_WSS_STATE_ACTIVE) { | 
|  | 717 | if (printk_ratelimit()) | 
|  | 718 | dev_err(dev, "WLP: Attempting to connect with " | 
|  | 719 | "WSS that is not active or connected.\n"); | 
|  | 720 | dev_kfree_skb(skb); | 
|  | 721 | goto out; | 
|  | 722 | } | 
|  | 723 | /* Establish connection - send C3 rcv C4 */ | 
|  | 724 | result = wlp_wss_connect_neighbor(wlp, wss, dev_addr); | 
|  | 725 | if (result < 0) { | 
|  | 726 | if (printk_ratelimit()) | 
|  | 727 | dev_err(dev, "WLP: Unable to establish connection " | 
|  | 728 | "with neighbor %02x:%02x.\n", | 
|  | 729 | dev_addr->data[1], dev_addr->data[0]); | 
|  | 730 | dev_kfree_skb(skb); | 
|  | 731 | goto out; | 
|  | 732 | } | 
|  | 733 | /* EDA entry changed, update the local copy being used */ | 
|  | 734 | result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry); | 
|  | 735 | if (result < 0) { | 
|  | 736 | if (printk_ratelimit()) | 
|  | 737 | dev_err(dev, "WLP: Cannot find EDA entry for " | 
|  | 738 | "neighbor %02x:%02x \n", | 
|  | 739 | dev_addr->data[1], dev_addr->data[0]); | 
|  | 740 | } | 
|  | 741 | result = wlp_wss_prep_hdr(wlp, eda_entry, skb); | 
|  | 742 | if (result < 0) { | 
|  | 743 | if (printk_ratelimit()) | 
|  | 744 | dev_err(dev, "WLP: Unable to prepare frame header for " | 
|  | 745 | "transmission (neighbor %02x:%02x). \n", | 
|  | 746 | dev_addr->data[1], dev_addr->data[0]); | 
|  | 747 | dev_kfree_skb(skb); | 
|  | 748 | goto out; | 
|  | 749 | } | 
|  | 750 | BUG_ON(wlp->xmit_frame == NULL); | 
|  | 751 | result = wlp->xmit_frame(wlp, skb, dev_addr); | 
|  | 752 | if (result < 0) { | 
|  | 753 | if (printk_ratelimit()) | 
|  | 754 | dev_err(dev, "WLP: Unable to transmit frame: %d\n", | 
|  | 755 | result); | 
|  | 756 | if (result == -ENXIO) | 
|  | 757 | dev_err(dev, "WLP: Is network interface up? \n"); | 
|  | 758 | /* We could try again ... */ | 
|  | 759 | dev_kfree_skb(skb);/*we need to free if tx fails */ | 
|  | 760 | } | 
|  | 761 | out: | 
|  | 762 | kfree(conn_ctx); | 
|  | 763 | BUG_ON(wlp->start_queue == NULL); | 
|  | 764 | wlp->start_queue(wlp); | 
|  | 765 | mutex_unlock(&wss->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 766 | } | 
|  | 767 |  | 
|  | 768 | /** | 
|  | 769 | * Add WLP header to outgoing skb | 
|  | 770 | * | 
|  | 771 | * @eda_entry: pointer to neighbor's entry in the EDA cache | 
|  | 772 | * @_skb:      skb containing data destined to the neighbor | 
|  | 773 | */ | 
|  | 774 | int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry, | 
|  | 775 | void *_skb) | 
|  | 776 | { | 
|  | 777 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 778 | int result = 0; | 
|  | 779 | unsigned char *eth_addr = eda_entry->eth_addr; | 
|  | 780 | struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; | 
|  | 781 | struct sk_buff *skb = _skb; | 
|  | 782 | struct wlp_frame_std_abbrv_hdr *std_hdr; | 
|  | 783 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 784 | if (eda_entry->state == WLP_WSS_CONNECTED) { | 
|  | 785 | /* Add WLP header */ | 
|  | 786 | BUG_ON(skb_headroom(skb) < sizeof(*std_hdr)); | 
|  | 787 | std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr)); | 
|  | 788 | std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID); | 
|  | 789 | std_hdr->hdr.type = WLP_FRAME_STANDARD; | 
|  | 790 | std_hdr->tag = eda_entry->wss->tag; | 
|  | 791 | } else { | 
|  | 792 | if (printk_ratelimit()) | 
|  | 793 | dev_err(dev, "WLP: Destination neighbor (Ethernet: " | 
|  | 794 | "%02x:%02x:%02x:%02x:%02x:%02x, Dev: " | 
|  | 795 | "%02x:%02x) is not connected. \n", eth_addr[0], | 
|  | 796 | eth_addr[1], eth_addr[2], eth_addr[3], | 
|  | 797 | eth_addr[4], eth_addr[5], dev_addr->data[1], | 
|  | 798 | dev_addr->data[0]); | 
|  | 799 | result = -EINVAL; | 
|  | 800 | } | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 801 | return result; | 
|  | 802 | } | 
|  | 803 |  | 
|  | 804 |  | 
|  | 805 | /** | 
|  | 806 | * Prepare skb for neighbor: connect if not already and prep WLP header | 
|  | 807 | * | 
|  | 808 | * This function is called in interrupt context, but it needs to sleep. We | 
|  | 809 | * temporarily stop the net queue to establish the WLP connection. | 
|  | 810 | * Setup of the WLP connection and restart of queue is scheduled | 
|  | 811 | * on the default work queue. | 
|  | 812 | * | 
|  | 813 | * run with eda->lock held (spinlock) | 
|  | 814 | */ | 
|  | 815 | int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry, | 
|  | 816 | void *_skb) | 
|  | 817 | { | 
|  | 818 | int result = 0; | 
|  | 819 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 820 | struct sk_buff *skb = _skb; | 
|  | 821 | struct wlp_assoc_conn_ctx *conn_ctx; | 
|  | 822 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 823 | if (eda_entry->state == WLP_WSS_UNCONNECTED) { | 
|  | 824 | /* We don't want any more packets while we set up connection */ | 
|  | 825 | BUG_ON(wlp->stop_queue == NULL); | 
|  | 826 | wlp->stop_queue(wlp); | 
|  | 827 | conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC); | 
|  | 828 | if (conn_ctx == NULL) { | 
|  | 829 | if (printk_ratelimit()) | 
|  | 830 | dev_err(dev, "WLP: Unable to allocate memory " | 
|  | 831 | "for connection handling.\n"); | 
|  | 832 | result = -ENOMEM; | 
|  | 833 | goto out; | 
|  | 834 | } | 
|  | 835 | conn_ctx->wlp = wlp; | 
|  | 836 | conn_ctx->skb = skb; | 
|  | 837 | conn_ctx->eda_entry = *eda_entry; | 
|  | 838 | INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send); | 
|  | 839 | schedule_work(&conn_ctx->ws); | 
|  | 840 | result = 1; | 
|  | 841 | } else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) { | 
|  | 842 | /* Previous connection attempts failed, don't retry - see | 
|  | 843 | * conditions for connection in WLP 0.99 [7.6.2] */ | 
|  | 844 | if (printk_ratelimit()) | 
|  | 845 | dev_err(dev, "Could not connect to neighbor " | 
|  | 846 | "previously. Not retrying. \n"); | 
|  | 847 | result = -ENONET; | 
|  | 848 | goto out; | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 849 | } else /* eda_entry->state == WLP_WSS_CONNECTED */ | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 850 | result = wlp_wss_prep_hdr(wlp, eda_entry, skb); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 851 | out: | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 852 | return result; | 
|  | 853 | } | 
|  | 854 |  | 
|  | 855 | /** | 
|  | 856 | * Emulate broadcast: copy skb, send copy to neighbor (connect if not already) | 
|  | 857 | * | 
|  | 858 | * We need to copy skbs in the case where we emulate broadcast through | 
|  | 859 | * unicast. We copy instead of clone because we are modifying the data of | 
|  | 860 | * the frame after copying ... clones share data so we cannot emulate | 
|  | 861 | * broadcast using clones. | 
|  | 862 | * | 
|  | 863 | * run with eda->lock held (spinlock) | 
|  | 864 | */ | 
|  | 865 | int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry, | 
|  | 866 | void *_skb) | 
|  | 867 | { | 
|  | 868 | int result = -ENOMEM; | 
|  | 869 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 870 | struct sk_buff *skb = _skb; | 
|  | 871 | struct sk_buff *copy; | 
|  | 872 | struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr; | 
|  | 873 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 874 | copy = skb_copy(skb, GFP_ATOMIC); | 
|  | 875 | if (copy == NULL) { | 
|  | 876 | if (printk_ratelimit()) | 
|  | 877 | dev_err(dev, "WLP: Unable to copy skb for " | 
|  | 878 | "transmission.\n"); | 
|  | 879 | goto out; | 
|  | 880 | } | 
|  | 881 | result = wlp_wss_connect_prep(wlp, eda_entry, copy); | 
|  | 882 | if (result < 0) { | 
|  | 883 | if (printk_ratelimit()) | 
|  | 884 | dev_err(dev, "WLP: Unable to connect/send skb " | 
|  | 885 | "to neighbor.\n"); | 
|  | 886 | dev_kfree_skb_irq(copy); | 
|  | 887 | goto out; | 
|  | 888 | } else if (result == 1) | 
|  | 889 | /* Frame will be transmitted separately */ | 
|  | 890 | goto out; | 
|  | 891 | BUG_ON(wlp->xmit_frame == NULL); | 
|  | 892 | result = wlp->xmit_frame(wlp, copy, dev_addr); | 
|  | 893 | if (result < 0) { | 
|  | 894 | if (printk_ratelimit()) | 
|  | 895 | dev_err(dev, "WLP: Unable to transmit frame: %d\n", | 
|  | 896 | result); | 
|  | 897 | if ((result == -ENXIO) && printk_ratelimit()) | 
|  | 898 | dev_err(dev, "WLP: Is network interface up? \n"); | 
|  | 899 | /* We could try again ... */ | 
|  | 900 | dev_kfree_skb_irq(copy);/*we need to free if tx fails */ | 
|  | 901 | } | 
|  | 902 | out: | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 903 | return result; | 
|  | 904 | } | 
|  | 905 |  | 
|  | 906 |  | 
|  | 907 | /** | 
|  | 908 | * Setup WSS | 
|  | 909 | * | 
|  | 910 | * Should be called by network driver after the interface has been given a | 
|  | 911 | * MAC address. | 
|  | 912 | */ | 
|  | 913 | int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss) | 
|  | 914 | { | 
|  | 915 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
|  | 916 | struct device *dev = &wlp->rc->uwb_dev.dev; | 
|  | 917 | int result = 0; | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 918 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 919 | mutex_lock(&wss->mutex); | 
|  | 920 | wss->kobj.parent = &net_dev->dev.kobj; | 
|  | 921 | if (!is_valid_ether_addr(net_dev->dev_addr)) { | 
|  | 922 | dev_err(dev, "WLP: Invalid MAC address. Cannot use for" | 
|  | 923 | "virtual.\n"); | 
|  | 924 | result = -EINVAL; | 
|  | 925 | goto out; | 
|  | 926 | } | 
|  | 927 | memcpy(wss->virtual_addr.data, net_dev->dev_addr, | 
|  | 928 | sizeof(wss->virtual_addr.data)); | 
|  | 929 | out: | 
|  | 930 | mutex_unlock(&wss->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 931 | return result; | 
|  | 932 | } | 
|  | 933 | EXPORT_SYMBOL_GPL(wlp_wss_setup); | 
|  | 934 |  | 
|  | 935 | /** | 
|  | 936 | * Remove WSS | 
|  | 937 | * | 
|  | 938 | * Called by client that configured WSS through wlp_wss_setup(). This | 
|  | 939 | * function is called when client no longer needs WSS, eg. client shuts | 
|  | 940 | * down. | 
|  | 941 | * | 
|  | 942 | * We remove the WLP IE from the beacon before initiating local cleanup. | 
|  | 943 | */ | 
|  | 944 | void wlp_wss_remove(struct wlp_wss *wss) | 
|  | 945 | { | 
|  | 946 | struct wlp *wlp = container_of(wss, struct wlp, wss); | 
| David Vrabel | bce8369 | 2008-12-22 18:22:50 +0000 | [diff] [blame] | 947 |  | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 948 | mutex_lock(&wss->mutex); | 
|  | 949 | if (wss->state == WLP_WSS_STATE_ACTIVE) | 
|  | 950 | uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP); | 
|  | 951 | if (wss->state != WLP_WSS_STATE_NONE) { | 
|  | 952 | sysfs_remove_group(&wss->kobj, &wss_attr_group); | 
|  | 953 | kobject_put(&wss->kobj); | 
|  | 954 | } | 
|  | 955 | wss->kobj.parent = NULL; | 
|  | 956 | memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr)); | 
|  | 957 | /* Cleanup EDA cache */ | 
|  | 958 | wlp_eda_release(&wlp->eda); | 
|  | 959 | wlp_eda_init(&wlp->eda); | 
|  | 960 | mutex_unlock(&wss->mutex); | 
| Reinette Chatre | 2f19204 | 2008-09-17 16:34:18 +0100 | [diff] [blame] | 961 | } | 
|  | 962 | EXPORT_SYMBOL_GPL(wlp_wss_remove); |