/drivers/uwb/est.c
C | 466 lines | 305 code | 25 blank | 136 comment | 33 complexity | 84b60c506be787c7d462ada19327c894 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
1/* 2 * Ultra Wide Band Radio Control 3 * Event Size Tables management 4 * 5 * Copyright (C) 2005-2006 Intel Corporation 6 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License version 10 * 2 as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 * 02110-1301, USA. 21 * 22 * 23 * FIXME: docs 24 * 25 * Infrastructure, code and data tables for guessing the size of 26 * events received on the notification endpoints of UWB radio 27 * controllers. 28 * 29 * You define a table of events and for each, its size and how to get 30 * the extra size. 31 * 32 * ENTRY POINTS: 33 * 34 * uwb_est_{init/destroy}(): To initialize/release the EST subsystem. 35 * 36 * uwb_est_[u]register(): To un/register event size tables 37 * uwb_est_grow() 38 * 39 * uwb_est_find_size(): Get the size of an event 40 * uwb_est_get_size() 41 */ 42#include <linux/spinlock.h> 43#include <linux/slab.h> 44 45#include "uwb-internal.h" 46 47struct uwb_est { 48 u16 type_event_high; 49 u16 vendor, product; 50 u8 entries; 51 const struct uwb_est_entry *entry; 52}; 53 54static struct uwb_est *uwb_est; 55static u8 uwb_est_size; 56static u8 uwb_est_used; 57static DEFINE_RWLOCK(uwb_est_lock); 58 59/** 60 * WUSB Standard Event Size Table, HWA-RC interface 61 * 62 * Sizes for events and notifications type 0 (general), high nibble 0. 63 */ 64static 65struct uwb_est_entry uwb_est_00_00xx[] = { 66 [UWB_RC_EVT_IE_RCV] = { 67 .size = sizeof(struct uwb_rc_evt_ie_rcv), 68 .offset = 1 + offsetof(struct uwb_rc_evt_ie_rcv, wIELength), 69 }, 70 [UWB_RC_EVT_BEACON] = { 71 .size = sizeof(struct uwb_rc_evt_beacon), 72 .offset = 1 + offsetof(struct uwb_rc_evt_beacon, wBeaconInfoLength), 73 }, 74 [UWB_RC_EVT_BEACON_SIZE] = { 75 .size = sizeof(struct uwb_rc_evt_beacon_size), 76 }, 77 [UWB_RC_EVT_BPOIE_CHANGE] = { 78 .size = sizeof(struct uwb_rc_evt_bpoie_change), 79 .offset = 1 + offsetof(struct uwb_rc_evt_bpoie_change, 80 wBPOIELength), 81 }, 82 [UWB_RC_EVT_BP_SLOT_CHANGE] = { 83 .size = sizeof(struct uwb_rc_evt_bp_slot_change), 84 }, 85 [UWB_RC_EVT_BP_SWITCH_IE_RCV] = { 86 .size = sizeof(struct uwb_rc_evt_bp_switch_ie_rcv), 87 .offset = 1 + offsetof(struct uwb_rc_evt_bp_switch_ie_rcv, wIELength), 88 }, 89 [UWB_RC_EVT_DEV_ADDR_CONFLICT] = { 90 .size = sizeof(struct uwb_rc_evt_dev_addr_conflict), 91 }, 92 [UWB_RC_EVT_DRP_AVAIL] = { 93 .size = sizeof(struct uwb_rc_evt_drp_avail) 94 }, 95 [UWB_RC_EVT_DRP] = { 96 .size = sizeof(struct uwb_rc_evt_drp), 97 .offset = 1 + offsetof(struct uwb_rc_evt_drp, ie_length), 98 }, 99 [UWB_RC_EVT_BP_SWITCH_STATUS] = { 100 .size = sizeof(struct uwb_rc_evt_bp_switch_status), 101 }, 102 [UWB_RC_EVT_CMD_FRAME_RCV] = { 103 .size = sizeof(struct uwb_rc_evt_cmd_frame_rcv), 104 .offset = 1 + offsetof(struct uwb_rc_evt_cmd_frame_rcv, dataLength), 105 }, 106 [UWB_RC_EVT_CHANNEL_CHANGE_IE_RCV] = { 107 .size = sizeof(struct uwb_rc_evt_channel_change_ie_rcv), 108 .offset = 1 + offsetof(struct uwb_rc_evt_channel_change_ie_rcv, wIELength), 109 }, 110 [UWB_RC_CMD_CHANNEL_CHANGE] = { 111 .size = sizeof(struct uwb_rc_evt_confirm), 112 }, 113 [UWB_RC_CMD_DEV_ADDR_MGMT] = { 114 .size = sizeof(struct uwb_rc_evt_dev_addr_mgmt) }, 115 [UWB_RC_CMD_GET_IE] = { 116 .size = sizeof(struct uwb_rc_evt_get_ie), 117 .offset = 1 + offsetof(struct uwb_rc_evt_get_ie, wIELength), 118 }, 119 [UWB_RC_CMD_RESET] = { 120 .size = sizeof(struct uwb_rc_evt_confirm), 121 }, 122 [UWB_RC_CMD_SCAN] = { 123 .size = sizeof(struct uwb_rc_evt_confirm), 124 }, 125 [UWB_RC_CMD_SET_BEACON_FILTER] = { 126 .size = sizeof(struct uwb_rc_evt_confirm), 127 }, 128 [UWB_RC_CMD_SET_DRP_IE] = { 129 .size = sizeof(struct uwb_rc_evt_set_drp_ie), 130 }, 131 [UWB_RC_CMD_SET_IE] = { 132 .size = sizeof(struct uwb_rc_evt_set_ie), 133 }, 134 [UWB_RC_CMD_SET_NOTIFICATION_FILTER] = { 135 .size = sizeof(struct uwb_rc_evt_confirm), 136 }, 137 [UWB_RC_CMD_SET_TX_POWER] = { 138 .size = sizeof(struct uwb_rc_evt_confirm), 139 }, 140 [UWB_RC_CMD_SLEEP] = { 141 .size = sizeof(struct uwb_rc_evt_confirm), 142 }, 143 [UWB_RC_CMD_START_BEACON] = { 144 .size = sizeof(struct uwb_rc_evt_confirm), 145 }, 146 [UWB_RC_CMD_STOP_BEACON] = { 147 .size = sizeof(struct uwb_rc_evt_confirm), 148 }, 149 [UWB_RC_CMD_BP_MERGE] = { 150 .size = sizeof(struct uwb_rc_evt_confirm), 151 }, 152 [UWB_RC_CMD_SEND_COMMAND_FRAME] = { 153 .size = sizeof(struct uwb_rc_evt_confirm), 154 }, 155 [UWB_RC_CMD_SET_ASIE_NOTIF] = { 156 .size = sizeof(struct uwb_rc_evt_confirm), 157 }, 158}; 159 160static 161struct uwb_est_entry uwb_est_01_00xx[] = { 162 [UWB_RC_DAA_ENERGY_DETECTED] = { 163 .size = sizeof(struct uwb_rc_evt_daa_energy_detected), 164 }, 165 [UWB_RC_SET_DAA_ENERGY_MASK] = { 166 .size = sizeof(struct uwb_rc_evt_set_daa_energy_mask), 167 }, 168 [UWB_RC_SET_NOTIFICATION_FILTER_EX] = { 169 .size = sizeof(struct uwb_rc_evt_set_notification_filter_ex), 170 }, 171}; 172 173/** 174 * Initialize the EST subsystem 175 * 176 * Register the standard tables also. 177 * 178 * FIXME: tag init 179 */ 180int uwb_est_create(void) 181{ 182 int result; 183 184 uwb_est_size = 2; 185 uwb_est_used = 0; 186 uwb_est = kzalloc(uwb_est_size * sizeof(uwb_est[0]), GFP_KERNEL); 187 if (uwb_est == NULL) 188 return -ENOMEM; 189 190 result = uwb_est_register(UWB_RC_CET_GENERAL, 0, 0xffff, 0xffff, 191 uwb_est_00_00xx, ARRAY_SIZE(uwb_est_00_00xx)); 192 if (result < 0) 193 goto out; 194 result = uwb_est_register(UWB_RC_CET_EX_TYPE_1, 0, 0xffff, 0xffff, 195 uwb_est_01_00xx, ARRAY_SIZE(uwb_est_01_00xx)); 196out: 197 return result; 198} 199 200 201/** Clean it up */ 202void uwb_est_destroy(void) 203{ 204 kfree(uwb_est); 205 uwb_est = NULL; 206 uwb_est_size = uwb_est_used = 0; 207} 208 209 210/** 211 * Double the capacity of the EST table 212 * 213 * @returns 0 if ok, < 0 errno no error. 214 */ 215static 216int uwb_est_grow(void) 217{ 218 size_t actual_size = uwb_est_size * sizeof(uwb_est[0]); 219 void *new = kmalloc(2 * actual_size, GFP_ATOMIC); 220 if (new == NULL) 221 return -ENOMEM; 222 memcpy(new, uwb_est, actual_size); 223 memset(new + actual_size, 0, actual_size); 224 kfree(uwb_est); 225 uwb_est = new; 226 uwb_est_size *= 2; 227 return 0; 228} 229 230 231/** 232 * Register an event size table 233 * 234 * Makes room for it if the table is full, and then inserts it in the 235 * right position (entries are sorted by type, event_high, vendor and 236 * then product). 237 * 238 * @vendor: vendor code for matching against the device (0x0000 and 239 * 0xffff mean any); use 0x0000 to force all to match without 240 * checking possible vendor specific ones, 0xfffff to match 241 * after checking vendor specific ones. 242 * 243 * @product: product code from that vendor; same matching rules, use 244 * 0x0000 for not allowing vendor specific matches, 0xffff 245 * for allowing. 246 * 247 * This arragement just makes the tables sort differenty. Because the 248 * table is sorted by growing type-event_high-vendor-product, a zero 249 * vendor will match before than a 0x456a vendor, that will match 250 * before a 0xfffff vendor. 251 * 252 * @returns 0 if ok, < 0 errno on error (-ENOENT if not found). 253 */ 254/* FIXME: add bus type to vendor/product code */ 255int uwb_est_register(u8 type, u8 event_high, u16 vendor, u16 product, 256 const struct uwb_est_entry *entry, size_t entries) 257{ 258 unsigned long flags; 259 unsigned itr; 260 u16 type_event_high; 261 int result = 0; 262 263 write_lock_irqsave(&uwb_est_lock, flags); 264 if (uwb_est_used == uwb_est_size) { 265 result = uwb_est_grow(); 266 if (result < 0) 267 goto out; 268 } 269 /* Find the right spot to insert it in */ 270 type_event_high = type << 8 | event_high; 271 for (itr = 0; itr < uwb_est_used; itr++) 272 if (uwb_est[itr].type_event_high < type 273 && uwb_est[itr].vendor < vendor 274 && uwb_est[itr].product < product) 275 break; 276 277 /* Shift others to make room for the new one? */ 278 if (itr < uwb_est_used) 279 memmove(&uwb_est[itr+1], &uwb_est[itr], uwb_est_used - itr); 280 uwb_est[itr].type_event_high = type << 8 | event_high; 281 uwb_est[itr].vendor = vendor; 282 uwb_est[itr].product = product; 283 uwb_est[itr].entry = entry; 284 uwb_est[itr].entries = entries; 285 uwb_est_used++; 286out: 287 write_unlock_irqrestore(&uwb_est_lock, flags); 288 return result; 289} 290EXPORT_SYMBOL_GPL(uwb_est_register); 291 292 293/** 294 * Unregister an event size table 295 * 296 * This just removes the specified entry and moves the ones after it 297 * to fill in the gap. This is needed to keep the list sorted; no 298 * reallocation is done to reduce the size of the table. 299 * 300 * We unregister by all the data we used to register instead of by 301 * pointer to the @entry array because we might have used the same 302 * table for a bunch of IDs (for example). 303 * 304 * @returns 0 if ok, < 0 errno on error (-ENOENT if not found). 305 */ 306int uwb_est_unregister(u8 type, u8 event_high, u16 vendor, u16 product, 307 const struct uwb_est_entry *entry, size_t entries) 308{ 309 unsigned long flags; 310 unsigned itr; 311 struct uwb_est est_cmp = { 312 .type_event_high = type << 8 | event_high, 313 .vendor = vendor, 314 .product = product, 315 .entry = entry, 316 .entries = entries 317 }; 318 write_lock_irqsave(&uwb_est_lock, flags); 319 for (itr = 0; itr < uwb_est_used; itr++) 320 if (!memcmp(&uwb_est[itr], &est_cmp, sizeof(est_cmp))) 321 goto found; 322 write_unlock_irqrestore(&uwb_est_lock, flags); 323 return -ENOENT; 324 325found: 326 if (itr < uwb_est_used - 1) /* Not last one? move ones above */ 327 memmove(&uwb_est[itr], &uwb_est[itr+1], uwb_est_used - itr - 1); 328 uwb_est_used--; 329 write_unlock_irqrestore(&uwb_est_lock, flags); 330 return 0; 331} 332EXPORT_SYMBOL_GPL(uwb_est_unregister); 333 334 335/** 336 * Get the size of an event from a table 337 * 338 * @rceb: pointer to the buffer with the event 339 * @rceb_size: size of the area pointed to by @rceb in bytes. 340 * @returns: > 0 Size of the event 341 * -ENOSPC An area big enough was not provided to look 342 * ahead into the event's guts and guess the size. 343 * -EINVAL Unknown event code (wEvent). 344 * 345 * This will look at the received RCEB and guess what is the total 346 * size. For variable sized events, it will look further ahead into 347 * their length field to see how much data should be read. 348 * 349 * Note this size is *not* final--the neh (Notification/Event Handle) 350 * might specificy an extra size to add. 351 */ 352static 353ssize_t uwb_est_get_size(struct uwb_rc *uwb_rc, struct uwb_est *est, 354 u8 event_low, const struct uwb_rceb *rceb, 355 size_t rceb_size) 356{ 357 unsigned offset; 358 ssize_t size; 359 struct device *dev = &uwb_rc->uwb_dev.dev; 360 const struct uwb_est_entry *entry; 361 362 size = -ENOENT; 363 if (event_low >= est->entries) { /* in range? */ 364 dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: event %u out of range\n", 365 est, est->type_event_high, est->vendor, est->product, 366 est->entries, event_low); 367 goto out; 368 } 369 size = -ENOENT; 370 entry = &est->entry[event_low]; 371 if (entry->size == 0 && entry->offset == 0) { /* unknown? */ 372 dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: event %u unknown\n", 373 est, est->type_event_high, est->vendor, est->product, 374 est->entries, event_low); 375 goto out; 376 } 377 offset = entry->offset; /* extra fries with that? */ 378 if (offset == 0) 379 size = entry->size; 380 else { 381 /* Ops, got an extra size field at 'offset'--read it */ 382 const void *ptr = rceb; 383 size_t type_size = 0; 384 offset--; 385 size = -ENOSPC; /* enough data for more? */ 386 switch (entry->type) { 387 case UWB_EST_16: type_size = sizeof(__le16); break; 388 case UWB_EST_8: type_size = sizeof(u8); break; 389 default: BUG(); 390 } 391 if (offset + type_size > rceb_size) { 392 dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: " 393 "not enough data to read extra size\n", 394 est, est->type_event_high, est->vendor, 395 est->product, est->entries); 396 goto out; 397 } 398 size = entry->size; 399 ptr += offset; 400 switch (entry->type) { 401 case UWB_EST_16: size += le16_to_cpu(*(__le16 *)ptr); break; 402 case UWB_EST_8: size += *(u8 *)ptr; break; 403 default: BUG(); 404 } 405 } 406out: 407 return size; 408} 409 410 411/** 412 * Guesses the size of a WA event 413 * 414 * @rceb: pointer to the buffer with the event 415 * @rceb_size: size of the area pointed to by @rceb in bytes. 416 * @returns: > 0 Size of the event 417 * -ENOSPC An area big enough was not provided to look 418 * ahead into the event's guts and guess the size. 419 * -EINVAL Unknown event code (wEvent). 420 * 421 * This will look at the received RCEB and guess what is the total 422 * size by checking all the tables registered with 423 * uwb_est_register(). For variable sized events, it will look further 424 * ahead into their length field to see how much data should be read. 425 * 426 * Note this size is *not* final--the neh (Notification/Event Handle) 427 * might specificy an extra size to add or replace. 428 */ 429ssize_t uwb_est_find_size(struct uwb_rc *rc, const struct uwb_rceb *rceb, 430 size_t rceb_size) 431{ 432 /* FIXME: add vendor/product data */ 433 ssize_t size; 434 struct device *dev = &rc->uwb_dev.dev; 435 unsigned long flags; 436 unsigned itr; 437 u16 type_event_high, event; 438 u8 *ptr = (u8 *) rceb; 439 440 read_lock_irqsave(&uwb_est_lock, flags); 441 size = -ENOSPC; 442 if (rceb_size < sizeof(*rceb)) 443 goto out; 444 event = le16_to_cpu(rceb->wEvent); 445 type_event_high = rceb->bEventType << 8 | (event & 0xff00) >> 8; 446 for (itr = 0; itr < uwb_est_used; itr++) { 447 if (uwb_est[itr].type_event_high != type_event_high) 448 continue; 449 size = uwb_est_get_size(rc, &uwb_est[itr], 450 event & 0x00ff, rceb, rceb_size); 451 /* try more tables that might handle the same type */ 452 if (size != -ENOENT) 453 goto out; 454 } 455 dev_dbg(dev, "event 0x%02x/%04x/%02x: no handlers available; " 456 "RCEB %02x %02x %02x %02x\n", 457 (unsigned) rceb->bEventType, 458 (unsigned) le16_to_cpu(rceb->wEvent), 459 (unsigned) rceb->bEventContext, 460 ptr[0], ptr[1], ptr[2], ptr[3]); 461 size = -ENOENT; 462out: 463 read_unlock_irqrestore(&uwb_est_lock, flags); 464 return size; 465} 466EXPORT_SYMBOL_GPL(uwb_est_find_size);