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 v4.16-rc2 303 lines 7.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) 4 * MMC (Microscheduled Management Command) handling 5 * 6 * Copyright (C) 2005-2006 Intel Corporation 7 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 8 * 9 * WUIEs and MMC IEs...well, they are almost the same at the end. MMC 10 * IEs are Wireless USB IEs that go into the MMC period...[what is 11 * that? look in Design-overview.txt]. 12 * 13 * 14 * This is a simple subsystem to keep track of which IEs are being 15 * sent by the host in the MMC period. 16 * 17 * For each WUIE we ask to send, we keep it in an array, so we can 18 * request its removal later, or replace the content. They are tracked 19 * by pointer, so be sure to use the same pointer if you want to 20 * remove it or update the contents. 21 * 22 * FIXME: 23 * - add timers that autoremove intervalled IEs? 24 */ 25#include <linux/usb/wusb.h> 26#include <linux/slab.h> 27#include <linux/export.h> 28#include "wusbhc.h" 29 30/* Initialize the MMCIEs handling mechanism */ 31int wusbhc_mmcie_create(struct wusbhc *wusbhc) 32{ 33 u8 mmcies = wusbhc->mmcies_max; 34 wusbhc->mmcie = kcalloc(mmcies, sizeof(wusbhc->mmcie[0]), GFP_KERNEL); 35 if (wusbhc->mmcie == NULL) 36 return -ENOMEM; 37 mutex_init(&wusbhc->mmcie_mutex); 38 return 0; 39} 40 41/* Release resources used by the MMCIEs handling mechanism */ 42void wusbhc_mmcie_destroy(struct wusbhc *wusbhc) 43{ 44 kfree(wusbhc->mmcie); 45} 46 47/* 48 * Add or replace an MMC Wireless USB IE. 49 * 50 * @interval: See WUSB1.0[8.5.3.1] 51 * @repeat_cnt: See WUSB1.0[8.5.3.1] 52 * @handle: See WUSB1.0[8.5.3.1] 53 * @wuie: Pointer to the header of the WUSB IE data to add. 54 * MUST BE allocated in a kmalloc buffer (no stack or 55 * vmalloc). 56 * THE CALLER ALWAYS OWNS THE POINTER (we don't free it 57 * on remove, we just forget about it). 58 * @returns: 0 if ok, < 0 errno code on error. 59 * 60 * Goes over the *whole* @wusbhc->mmcie array looking for (a) the 61 * first free spot and (b) if @wuie is already in the array (aka: 62 * transmitted in the MMCs) the spot were it is. 63 * 64 * If present, we "overwrite it" (update). 65 * 66 * 67 * NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38. 68 * The host uses the handle as the 'sort' index. We 69 * allocate the last one always for the WUIE_ID_HOST_INFO, and 70 * the rest, first come first serve in inverse order. 71 * 72 * Host software must make sure that it adds the other IEs in 73 * the right order... the host hardware is responsible for 74 * placing the WCTA IEs in the right place with the other IEs 75 * set by host software. 76 * 77 * NOTE: we can access wusbhc->wa_descr without locking because it is 78 * read only. 79 */ 80int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, 81 struct wuie_hdr *wuie) 82{ 83 int result = -ENOBUFS; 84 unsigned handle, itr; 85 86 /* Search a handle, taking into account the ordering */ 87 mutex_lock(&wusbhc->mmcie_mutex); 88 switch (wuie->bIEIdentifier) { 89 case WUIE_ID_HOST_INFO: 90 /* Always last */ 91 handle = wusbhc->mmcies_max - 1; 92 break; 93 case WUIE_ID_ISOCH_DISCARD: 94 dev_err(wusbhc->dev, "Special ordering case for WUIE ID 0x%x " 95 "unimplemented\n", wuie->bIEIdentifier); 96 result = -ENOSYS; 97 goto error_unlock; 98 default: 99 /* search for it or find the last empty slot */ 100 handle = ~0; 101 for (itr = 0; itr < wusbhc->mmcies_max - 1; itr++) { 102 if (wusbhc->mmcie[itr] == wuie) { 103 handle = itr; 104 break; 105 } 106 if (wusbhc->mmcie[itr] == NULL) 107 handle = itr; 108 } 109 if (handle == ~0) 110 goto error_unlock; 111 } 112 result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle, 113 wuie); 114 if (result >= 0) 115 wusbhc->mmcie[handle] = wuie; 116error_unlock: 117 mutex_unlock(&wusbhc->mmcie_mutex); 118 return result; 119} 120EXPORT_SYMBOL_GPL(wusbhc_mmcie_set); 121 122/* 123 * Remove an MMC IE previously added with wusbhc_mmcie_set() 124 * 125 * @wuie Pointer used to add the WUIE 126 */ 127void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie) 128{ 129 int result; 130 unsigned handle, itr; 131 132 mutex_lock(&wusbhc->mmcie_mutex); 133 for (itr = 0; itr < wusbhc->mmcies_max; itr++) { 134 if (wusbhc->mmcie[itr] == wuie) { 135 handle = itr; 136 goto found; 137 } 138 } 139 mutex_unlock(&wusbhc->mmcie_mutex); 140 return; 141 142found: 143 result = (wusbhc->mmcie_rm)(wusbhc, handle); 144 if (result == 0) 145 wusbhc->mmcie[itr] = NULL; 146 mutex_unlock(&wusbhc->mmcie_mutex); 147} 148EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm); 149 150static int wusbhc_mmc_start(struct wusbhc *wusbhc) 151{ 152 int ret; 153 154 mutex_lock(&wusbhc->mutex); 155 ret = wusbhc->start(wusbhc); 156 if (ret >= 0) 157 wusbhc->active = 1; 158 mutex_unlock(&wusbhc->mutex); 159 160 return ret; 161} 162 163static void wusbhc_mmc_stop(struct wusbhc *wusbhc) 164{ 165 mutex_lock(&wusbhc->mutex); 166 wusbhc->active = 0; 167 wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS); 168 mutex_unlock(&wusbhc->mutex); 169} 170 171/* 172 * wusbhc_start - start transmitting MMCs and accepting connections 173 * @wusbhc: the HC to start 174 * 175 * Establishes a cluster reservation, enables device connections, and 176 * starts MMCs with appropriate DNTS parameters. 177 */ 178int wusbhc_start(struct wusbhc *wusbhc) 179{ 180 int result; 181 struct device *dev = wusbhc->dev; 182 183 WARN_ON(wusbhc->wuie_host_info != NULL); 184 BUG_ON(wusbhc->uwb_rc == NULL); 185 186 result = wusbhc_rsv_establish(wusbhc); 187 if (result < 0) { 188 dev_err(dev, "cannot establish cluster reservation: %d\n", 189 result); 190 goto error_rsv_establish; 191 } 192 193 result = wusbhc_devconnect_start(wusbhc); 194 if (result < 0) { 195 dev_err(dev, "error enabling device connections: %d\n", 196 result); 197 goto error_devconnect_start; 198 } 199 200 result = wusbhc_sec_start(wusbhc); 201 if (result < 0) { 202 dev_err(dev, "error starting security in the HC: %d\n", 203 result); 204 goto error_sec_start; 205 } 206 207 result = wusbhc->set_num_dnts(wusbhc, wusbhc->dnts_interval, 208 wusbhc->dnts_num_slots); 209 if (result < 0) { 210 dev_err(dev, "Cannot set DNTS parameters: %d\n", result); 211 goto error_set_num_dnts; 212 } 213 result = wusbhc_mmc_start(wusbhc); 214 if (result < 0) { 215 dev_err(dev, "error starting wusbch: %d\n", result); 216 goto error_wusbhc_start; 217 } 218 219 return 0; 220 221error_wusbhc_start: 222 wusbhc_sec_stop(wusbhc); 223error_set_num_dnts: 224error_sec_start: 225 wusbhc_devconnect_stop(wusbhc); 226error_devconnect_start: 227 wusbhc_rsv_terminate(wusbhc); 228error_rsv_establish: 229 return result; 230} 231 232/* 233 * wusbhc_stop - stop transmitting MMCs 234 * @wusbhc: the HC to stop 235 * 236 * Stops the WUSB channel and removes the cluster reservation. 237 */ 238void wusbhc_stop(struct wusbhc *wusbhc) 239{ 240 wusbhc_mmc_stop(wusbhc); 241 wusbhc_sec_stop(wusbhc); 242 wusbhc_devconnect_stop(wusbhc); 243 wusbhc_rsv_terminate(wusbhc); 244} 245 246/* 247 * Set/reset/update a new CHID 248 * 249 * Depending on the previous state of the MMCs, start, stop or change 250 * the sent MMC. This effectively switches the host controller on and 251 * off (radio wise). 252 */ 253int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) 254{ 255 int result = 0; 256 257 if (memcmp(chid, &wusb_ckhdid_zero, sizeof(*chid)) == 0) 258 chid = NULL; 259 260 mutex_lock(&wusbhc->mutex); 261 if (chid) { 262 if (wusbhc->active) { 263 mutex_unlock(&wusbhc->mutex); 264 return -EBUSY; 265 } 266 wusbhc->chid = *chid; 267 } 268 269 /* register with UWB if we haven't already since we are about to start 270 the radio. */ 271 if ((chid) && (wusbhc->uwb_rc == NULL)) { 272 wusbhc->uwb_rc = uwb_rc_get_by_grandpa(wusbhc->dev->parent); 273 if (wusbhc->uwb_rc == NULL) { 274 result = -ENODEV; 275 dev_err(wusbhc->dev, 276 "Cannot get associated UWB Host Controller\n"); 277 goto error_rc_get; 278 } 279 280 result = wusbhc_pal_register(wusbhc); 281 if (result < 0) { 282 dev_err(wusbhc->dev, "Cannot register as a UWB PAL\n"); 283 goto error_pal_register; 284 } 285 } 286 mutex_unlock(&wusbhc->mutex); 287 288 if (chid) 289 result = uwb_radio_start(&wusbhc->pal); 290 else if (wusbhc->uwb_rc) 291 uwb_radio_stop(&wusbhc->pal); 292 293 return result; 294 295error_pal_register: 296 uwb_rc_put(wusbhc->uwb_rc); 297 wusbhc->uwb_rc = NULL; 298error_rc_get: 299 mutex_unlock(&wusbhc->mutex); 300 301 return result; 302} 303EXPORT_SYMBOL_GPL(wusbhc_chid_set);