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 v3.1-rc7 286 lines 7.8 kB view raw
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 "wusbhc.h" 42 43/* Initialize the MMCIEs handling mechanism */ 44int wusbhc_mmcie_create(struct wusbhc *wusbhc) 45{ 46 u8 mmcies = wusbhc->mmcies_max; 47 wusbhc->mmcie = kcalloc(mmcies, sizeof(wusbhc->mmcie[0]), GFP_KERNEL); 48 if (wusbhc->mmcie == NULL) 49 return -ENOMEM; 50 mutex_init(&wusbhc->mmcie_mutex); 51 return 0; 52} 53 54/* Release resources used by the MMCIEs handling mechanism */ 55void wusbhc_mmcie_destroy(struct wusbhc *wusbhc) 56{ 57 kfree(wusbhc->mmcie); 58} 59 60/* 61 * Add or replace an MMC Wireless USB IE. 62 * 63 * @interval: See WUSB1.0[8.5.3.1] 64 * @repeat_cnt: See WUSB1.0[8.5.3.1] 65 * @handle: See WUSB1.0[8.5.3.1] 66 * @wuie: Pointer to the header of the WUSB IE data to add. 67 * MUST BE allocated in a kmalloc buffer (no stack or 68 * vmalloc). 69 * THE CALLER ALWAYS OWNS THE POINTER (we don't free it 70 * on remove, we just forget about it). 71 * @returns: 0 if ok, < 0 errno code on error. 72 * 73 * Goes over the *whole* @wusbhc->mmcie array looking for (a) the 74 * first free spot and (b) if @wuie is already in the array (aka: 75 * transmitted in the MMCs) the spot were it is. 76 * 77 * If present, we "overwrite it" (update). 78 * 79 * 80 * NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38. 81 * The host uses the handle as the 'sort' index. We 82 * allocate the last one always for the WUIE_ID_HOST_INFO, and 83 * the rest, first come first serve in inverse order. 84 * 85 * Host software must make sure that it adds the other IEs in 86 * the right order... the host hardware is responsible for 87 * placing the WCTA IEs in the right place with the other IEs 88 * set by host software. 89 * 90 * NOTE: we can access wusbhc->wa_descr without locking because it is 91 * read only. 92 */ 93int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, 94 struct wuie_hdr *wuie) 95{ 96 int result = -ENOBUFS; 97 unsigned handle, itr; 98 99 /* Search a handle, taking into account the ordering */ 100 mutex_lock(&wusbhc->mmcie_mutex); 101 switch (wuie->bIEIdentifier) { 102 case WUIE_ID_HOST_INFO: 103 /* Always last */ 104 handle = wusbhc->mmcies_max - 1; 105 break; 106 case WUIE_ID_ISOCH_DISCARD: 107 dev_err(wusbhc->dev, "Special ordering case for WUIE ID 0x%x " 108 "unimplemented\n", wuie->bIEIdentifier); 109 result = -ENOSYS; 110 goto error_unlock; 111 default: 112 /* search for it or find the last empty slot */ 113 handle = ~0; 114 for (itr = 0; itr < wusbhc->mmcies_max - 1; itr++) { 115 if (wusbhc->mmcie[itr] == wuie) { 116 handle = itr; 117 break; 118 } 119 if (wusbhc->mmcie[itr] == NULL) 120 handle = itr; 121 } 122 if (handle == ~0) 123 goto error_unlock; 124 } 125 result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle, 126 wuie); 127 if (result >= 0) 128 wusbhc->mmcie[handle] = wuie; 129error_unlock: 130 mutex_unlock(&wusbhc->mmcie_mutex); 131 return result; 132} 133EXPORT_SYMBOL_GPL(wusbhc_mmcie_set); 134 135/* 136 * Remove an MMC IE previously added with wusbhc_mmcie_set() 137 * 138 * @wuie Pointer used to add the WUIE 139 */ 140void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie) 141{ 142 int result; 143 unsigned handle, itr; 144 145 mutex_lock(&wusbhc->mmcie_mutex); 146 for (itr = 0; itr < wusbhc->mmcies_max; itr++) { 147 if (wusbhc->mmcie[itr] == wuie) { 148 handle = itr; 149 goto found; 150 } 151 } 152 mutex_unlock(&wusbhc->mmcie_mutex); 153 return; 154 155found: 156 result = (wusbhc->mmcie_rm)(wusbhc, handle); 157 if (result == 0) 158 wusbhc->mmcie[itr] = NULL; 159 mutex_unlock(&wusbhc->mmcie_mutex); 160} 161EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm); 162 163static int wusbhc_mmc_start(struct wusbhc *wusbhc) 164{ 165 int ret; 166 167 mutex_lock(&wusbhc->mutex); 168 ret = wusbhc->start(wusbhc); 169 if (ret >= 0) 170 wusbhc->active = 1; 171 mutex_unlock(&wusbhc->mutex); 172 173 return ret; 174} 175 176static void wusbhc_mmc_stop(struct wusbhc *wusbhc) 177{ 178 mutex_lock(&wusbhc->mutex); 179 wusbhc->active = 0; 180 wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS); 181 mutex_unlock(&wusbhc->mutex); 182} 183 184/* 185 * wusbhc_start - start transmitting MMCs and accepting connections 186 * @wusbhc: the HC to start 187 * 188 * Establishes a cluster reservation, enables device connections, and 189 * starts MMCs with appropriate DNTS parameters. 190 */ 191int wusbhc_start(struct wusbhc *wusbhc) 192{ 193 int result; 194 struct device *dev = wusbhc->dev; 195 196 WARN_ON(wusbhc->wuie_host_info != NULL); 197 198 result = wusbhc_rsv_establish(wusbhc); 199 if (result < 0) { 200 dev_err(dev, "cannot establish cluster reservation: %d\n", 201 result); 202 goto error_rsv_establish; 203 } 204 205 result = wusbhc_devconnect_start(wusbhc); 206 if (result < 0) { 207 dev_err(dev, "error enabling device connections: %d\n", result); 208 goto error_devconnect_start; 209 } 210 211 result = wusbhc_sec_start(wusbhc); 212 if (result < 0) { 213 dev_err(dev, "error starting security in the HC: %d\n", result); 214 goto error_sec_start; 215 } 216 /* FIXME: the choice of the DNTS parameters is somewhat 217 * arbitrary */ 218 result = wusbhc->set_num_dnts(wusbhc, 0, 15); 219 if (result < 0) { 220 dev_err(dev, "Cannot set DNTS parameters: %d\n", result); 221 goto error_set_num_dnts; 222 } 223 result = wusbhc_mmc_start(wusbhc); 224 if (result < 0) { 225 dev_err(dev, "error starting wusbch: %d\n", result); 226 goto error_wusbhc_start; 227 } 228 229 return 0; 230 231error_wusbhc_start: 232 wusbhc_sec_stop(wusbhc); 233error_set_num_dnts: 234error_sec_start: 235 wusbhc_devconnect_stop(wusbhc); 236error_devconnect_start: 237 wusbhc_rsv_terminate(wusbhc); 238error_rsv_establish: 239 return result; 240} 241 242/* 243 * wusbhc_stop - stop transmitting MMCs 244 * @wusbhc: the HC to stop 245 * 246 * Stops the WUSB channel and removes the cluster reservation. 247 */ 248void wusbhc_stop(struct wusbhc *wusbhc) 249{ 250 wusbhc_mmc_stop(wusbhc); 251 wusbhc_sec_stop(wusbhc); 252 wusbhc_devconnect_stop(wusbhc); 253 wusbhc_rsv_terminate(wusbhc); 254} 255 256/* 257 * Set/reset/update a new CHID 258 * 259 * Depending on the previous state of the MMCs, start, stop or change 260 * the sent MMC. This effectively switches the host controller on and 261 * off (radio wise). 262 */ 263int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) 264{ 265 int result = 0; 266 267 if (memcmp(chid, &wusb_ckhdid_zero, sizeof(*chid)) == 0) 268 chid = NULL; 269 270 mutex_lock(&wusbhc->mutex); 271 if (chid) { 272 if (wusbhc->active) { 273 mutex_unlock(&wusbhc->mutex); 274 return -EBUSY; 275 } 276 wusbhc->chid = *chid; 277 } 278 mutex_unlock(&wusbhc->mutex); 279 280 if (chid) 281 result = uwb_radio_start(&wusbhc->pal); 282 else 283 uwb_radio_stop(&wusbhc->pal); 284 return result; 285} 286EXPORT_SYMBOL_GPL(wusbhc_chid_set);