]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/usb/wusbcore/mmc.c
e5390b77aaaaf6ee12895d89f941b681680b6682
[net-next-2.6.git] / drivers / usb / wusbcore / mmc.c
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 "wusbhc.h"
41
42 /* Initialize the MMCIEs handling mechanism */
43 int wusbhc_mmcie_create(struct wusbhc *wusbhc)
44 {
45         u8 mmcies = wusbhc->mmcies_max;
46         wusbhc->mmcie = kzalloc(mmcies * sizeof(wusbhc->mmcie[0]), GFP_KERNEL);
47         if (wusbhc->mmcie == NULL)
48                 return -ENOMEM;
49         mutex_init(&wusbhc->mmcie_mutex);
50         return 0;
51 }
52
53 /* Release resources used by the MMCIEs handling mechanism */
54 void wusbhc_mmcie_destroy(struct wusbhc *wusbhc)
55 {
56         kfree(wusbhc->mmcie);
57 }
58
59 /*
60  * Add or replace an MMC Wireless USB IE.
61  *
62  * @interval:    See WUSB1.0[8.5.3.1]
63  * @repeat_cnt:  See WUSB1.0[8.5.3.1]
64  * @handle:      See WUSB1.0[8.5.3.1]
65  * @wuie:        Pointer to the header of the WUSB IE data to add.
66  *               MUST BE allocated in a kmalloc buffer (no stack or
67  *               vmalloc).
68  *               THE CALLER ALWAYS OWNS THE POINTER (we don't free it
69  *               on remove, we just forget about it).
70  * @returns:     0 if ok, < 0 errno code on error.
71  *
72  * Goes over the *whole* @wusbhc->mmcie array looking for (a) the
73  * first free spot and (b) if @wuie is already in the array (aka:
74  * transmitted in the MMCs) the spot were it is.
75  *
76  * If present, we "overwrite it" (update).
77  *
78  *
79  * NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38.
80  *       The host uses the handle as the 'sort' index. We
81  *       allocate the last one always for the WUIE_ID_HOST_INFO, and
82  *       the rest, first come first serve in inverse order.
83  *
84  *       Host software must make sure that it adds the other IEs in
85  *       the right order... the host hardware is responsible for
86  *       placing the WCTA IEs in the right place with the other IEs
87  *       set by host software.
88  *
89  * NOTE: we can access wusbhc->wa_descr without locking because it is
90  *       read only.
91  */
92 int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
93                      struct wuie_hdr *wuie)
94 {
95         int result = -ENOBUFS;
96         struct device *dev = wusbhc->dev;
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                         if (printk_ratelimit())
124                                 dev_err(dev, "MMC handle space exhausted\n");
125                         goto error_unlock;
126                 }
127         }
128         result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle,
129                                      wuie);
130         if (result >= 0)
131                 wusbhc->mmcie[handle] = wuie;
132 error_unlock:
133         mutex_unlock(&wusbhc->mmcie_mutex);
134         return result;
135 }
136 EXPORT_SYMBOL_GPL(wusbhc_mmcie_set);
137
138 /*
139  * Remove an MMC IE previously added with wusbhc_mmcie_set()
140  *
141  * @wuie        Pointer used to add the WUIE
142  */
143 void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie)
144 {
145         int result;
146         struct device *dev = wusbhc->dev;
147         unsigned handle, itr;
148
149         mutex_lock(&wusbhc->mmcie_mutex);
150         for (itr = 0; itr < wusbhc->mmcies_max; itr++)
151                 if (wusbhc->mmcie[itr] == wuie) {
152                         handle = itr;
153                         goto found;
154                 }
155         mutex_unlock(&wusbhc->mmcie_mutex);
156         return;
157
158 found:
159         result = (wusbhc->mmcie_rm)(wusbhc, handle);
160         if (result == 0)
161                 wusbhc->mmcie[itr] = NULL;
162         else if (printk_ratelimit())
163                 dev_err(dev, "MMC: Failed to remove IE %p (0x%02x)\n",
164                         wuie, wuie->bIEIdentifier);
165         mutex_unlock(&wusbhc->mmcie_mutex);
166         return;
167 }
168 EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm);
169
170 /*
171  * wusbhc_start - start transmitting MMCs and accepting connections
172  * @wusbhc: the HC to start
173  * @chid: the CHID to use for this host
174  *
175  * Establishes a cluster reservation, enables device connections, and
176  * starts MMCs with appropriate DNTS parameters.
177  */
178 int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
179 {
180         int result;
181         struct device *dev = wusbhc->dev;
182
183         WARN_ON(wusbhc->wuie_host_info != NULL);
184
185         result = wusbhc_rsv_establish(wusbhc);
186         if (result < 0) {
187                 dev_err(dev, "cannot establish cluster reservation: %d\n",
188                         result);
189                 goto error_rsv_establish;
190         }
191
192         result = wusbhc_devconnect_start(wusbhc, chid);
193         if (result < 0) {
194                 dev_err(dev, "error enabling device connections: %d\n", result);
195                 goto error_devconnect_start;
196         }
197
198         result = wusbhc_sec_start(wusbhc);
199         if (result < 0) {
200                 dev_err(dev, "error starting security in the HC: %d\n", result);
201                 goto error_sec_start;
202         }
203         /* FIXME: the choice of the DNTS parameters is somewhat
204          * arbitrary */
205         result = wusbhc->set_num_dnts(wusbhc, 0, 15);
206         if (result < 0) {
207                 dev_err(dev, "Cannot set DNTS parameters: %d\n", result);
208                 goto error_set_num_dnts;
209         }
210         result = wusbhc->start(wusbhc);
211         if (result < 0) {
212                 dev_err(dev, "error starting wusbch: %d\n", result);
213                 goto error_wusbhc_start;
214         }
215         wusbhc->active = 1;
216         return 0;
217
218 error_wusbhc_start:
219         wusbhc_sec_stop(wusbhc);
220 error_set_num_dnts:
221 error_sec_start:
222         wusbhc_devconnect_stop(wusbhc);
223 error_devconnect_start:
224         wusbhc_rsv_terminate(wusbhc);
225 error_rsv_establish:
226         return result;
227 }
228
229 /*
230  * Disconnect all from the WUSB Channel
231  *
232  * Send a Host Disconnect IE in the MMC, wait, don't send it any more
233  */
234 static int __wusbhc_host_disconnect_ie(struct wusbhc *wusbhc)
235 {
236         int result = -ENOMEM;
237         struct wuie_host_disconnect *host_disconnect_ie;
238         might_sleep();
239         host_disconnect_ie = kmalloc(sizeof(*host_disconnect_ie), GFP_KERNEL);
240         if (host_disconnect_ie == NULL)
241                 goto error_alloc;
242         host_disconnect_ie->hdr.bLength       = sizeof(*host_disconnect_ie);
243         host_disconnect_ie->hdr.bIEIdentifier = WUIE_ID_HOST_DISCONNECT;
244         result = wusbhc_mmcie_set(wusbhc, 0, 0, &host_disconnect_ie->hdr);
245         if (result < 0)
246                 goto error_mmcie_set;
247
248         /* WUSB1.0[8.5.3.1 & 7.5.2] */
249         msleep(100);
250         wusbhc_mmcie_rm(wusbhc, &host_disconnect_ie->hdr);
251 error_mmcie_set:
252         kfree(host_disconnect_ie);
253 error_alloc:
254         return result;
255 }
256
257 /*
258  * wusbhc_stop - stop transmitting MMCs
259  * @wusbhc: the HC to stop
260  *
261  * Send a Host Disconnect IE, wait, remove all the MMCs (stop sending MMCs).
262  *
263  * If we can't allocate a Host Stop IE, screw it, we don't notify the
264  * devices we are disconnecting...
265  */
266 void wusbhc_stop(struct wusbhc *wusbhc)
267 {
268         if (wusbhc->active) {
269                 wusbhc->active = 0;
270                 wusbhc->stop(wusbhc);
271                 wusbhc_sec_stop(wusbhc);
272                 __wusbhc_host_disconnect_ie(wusbhc);
273                 wusbhc_devconnect_stop(wusbhc);
274                 wusbhc_rsv_terminate(wusbhc);
275         }
276 }
277 EXPORT_SYMBOL_GPL(wusbhc_stop);
278
279 /*
280  * Change the CHID in a WUSB Channel
281  *
282  * If it is just a new CHID, send a Host Disconnect IE and then change
283  * the CHID IE.
284  */
285 static int __wusbhc_chid_change(struct wusbhc *wusbhc,
286                                 const struct wusb_ckhdid *chid)
287 {
288         int result = -ENOSYS;
289         struct device *dev = wusbhc->dev;
290         dev_err(dev, "%s() not implemented yet\n", __func__);
291         return result;
292
293         BUG_ON(wusbhc->wuie_host_info == NULL);
294         __wusbhc_host_disconnect_ie(wusbhc);
295         wusbhc->wuie_host_info->CHID = *chid;
296         result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->wuie_host_info->hdr);
297         if (result < 0)
298                 dev_err(dev, "Can't update Host Info WUSB IE: %d\n", result);
299         return result;
300 }
301
302 /*
303  * Set/reset/update a new CHID
304  *
305  * Depending on the previous state of the MMCs, start, stop or change
306  * the sent MMC. This effectively switches the host controller on and
307  * off (radio wise).
308  */
309 int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
310 {
311         int result = 0;
312
313         if (memcmp(chid, &wusb_ckhdid_zero, sizeof(chid)) == 0)
314                 chid = NULL;
315
316         mutex_lock(&wusbhc->mutex);
317         if (wusbhc->active) {
318                 if (chid)
319                         result = __wusbhc_chid_change(wusbhc, chid);
320                 else
321                         wusbhc_stop(wusbhc);
322         } else {
323                 if (chid)
324                         wusbhc_start(wusbhc, chid);
325         }
326         mutex_unlock(&wusbhc->mutex);
327         return result;
328 }
329 EXPORT_SYMBOL_GPL(wusbhc_chid_set);