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