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