Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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