aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/whci/wusb.c
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@csr.com>2008-09-17 11:34:28 -0400
committerDavid Vrabel <dv02@dv02pc01.europe.root.pri>2008-09-17 11:54:31 -0400
commit7e6133aa42920ea87ad9791a0fb2b95d1a23b8f9 (patch)
treeaa686de823ea3246b6c5cd2e4ef0e54e3797c3e3 /drivers/usb/host/whci/wusb.c
parent870d5395045bfe8e5213525152682c863a10f8d2 (diff)
wusb: WHCI host controller driver
A driver for Wireless USB host controllers that comply with the Wireless Host Controller Interface (HCI) specification as published by Intel. The latest publically available version of the specification (0.95) is supported (except for isochronous transfers). Build fixes by Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: David Vrabel <david.vrabel@csr.com>
Diffstat (limited to 'drivers/usb/host/whci/wusb.c')
-rw-r--r--drivers/usb/host/whci/wusb.c241
1 files changed, 241 insertions, 0 deletions
diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c
new file mode 100644
index 000000000000..66e4ddcd961d
--- /dev/null
+++ b/drivers/usb/host/whci/wusb.c
@@ -0,0 +1,241 @@
1/*
2 * Wireless Host Controller (WHC) WUSB operations.
3 *
4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18#include <linux/version.h>
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/uwb/umc.h>
22#define D_LOCAL 1
23#include <linux/uwb/debug.h>
24
25#include "../../wusbcore/wusbhc.h"
26
27#include "whcd.h"
28
29#if D_LOCAL >= 1
30static void dump_di(struct whc *whc, int idx)
31{
32 struct di_buf_entry *di = &whc->di_buf[idx];
33 struct device *dev = &whc->umc->dev;
34 char buf[128];
35
36 bitmap_scnprintf(buf, sizeof(buf), (unsigned long *)di->availability_info, UWB_NUM_MAS);
37
38 d_printf(1, dev, "DI[%d]\n", idx);
39 d_printf(1, dev, " availability: %s\n", buf);
40 d_printf(1, dev, " %c%c key idx: %d dev addr: %d\n",
41 (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ',
42 (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ',
43 (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8,
44 (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK));
45}
46#else
47static inline void dump_di(struct whc *whc, int idx)
48{
49}
50#endif
51
52static int whc_update_di(struct whc *whc, int idx)
53{
54 int offset = idx / 32;
55 u32 bit = 1 << (idx % 32);
56
57 dump_di(whc, idx);
58
59 le_writel(bit, whc->base + WUSBDIBUPDATED + offset);
60
61 return whci_wait_for(&whc->umc->dev,
62 whc->base + WUSBDIBUPDATED + offset, bit, 0,
63 100, "DI update");
64}
65
66/*
67 * WHCI starts and stops MMCs based on there being a valid GTK so
68 * these need only start/stop the asynchronous and periodic schedules.
69 */
70
71int whc_wusbhc_start(struct wusbhc *wusbhc)
72{
73 struct whc *whc = wusbhc_to_whc(wusbhc);
74
75 asl_start(whc);
76 pzl_start(whc);
77
78 return 0;
79}
80
81void whc_wusbhc_stop(struct wusbhc *wusbhc)
82{
83 struct whc *whc = wusbhc_to_whc(wusbhc);
84
85 pzl_stop(whc);
86 asl_stop(whc);
87}
88
89int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
90 u8 handle, struct wuie_hdr *wuie)
91{
92 struct whc *whc = wusbhc_to_whc(wusbhc);
93 u32 params;
94
95 params = (interval << 24)
96 | (repeat_cnt << 16)
97 | (wuie->bLength << 8)
98 | handle;
99
100 return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength);
101}
102
103int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
104{
105 struct whc *whc = wusbhc_to_whc(wusbhc);
106 u32 params;
107
108 params = handle;
109
110 return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0);
111}
112
113int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm)
114{
115 struct whc *whc = wusbhc_to_whc(wusbhc);
116
117 if (stream_index >= 0)
118 whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index));
119
120 return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm));
121}
122
123int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
124{
125 struct whc *whc = wusbhc_to_whc(wusbhc);
126 int idx = wusb_dev->port_idx;
127 struct di_buf_entry *di = &whc->di_buf[idx];
128 int ret;
129
130 mutex_lock(&whc->mutex);
131
132 uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability);
133 di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK);
134 di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr);
135
136 ret = whc_update_di(whc, idx);
137
138 mutex_unlock(&whc->mutex);
139
140 return ret;
141}
142
143/*
144 * Set the number of Device Notification Time Slots (DNTS) and enable
145 * device notifications.
146 */
147int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
148{
149 struct whc *whc = wusbhc_to_whc(wusbhc);
150 u32 dntsctrl;
151
152 dntsctrl = WUSBDNTSCTRL_ACTIVE
153 | WUSBDNTSCTRL_INTERVAL(interval)
154 | WUSBDNTSCTRL_SLOTS(slots);
155
156 le_writel(dntsctrl, whc->base + WUSBDNTSCTRL);
157
158 return 0;
159}
160
161static int whc_set_key(struct whc *whc, u8 key_index, uint32_t tkid,
162 const void *key, size_t key_size, bool is_gtk)
163{
164 uint32_t setkeycmd;
165 uint32_t seckey[4];
166 int i;
167 int ret;
168
169 memcpy(seckey, key, key_size);
170 setkeycmd = WUSBSETSECKEYCMD_SET | WUSBSETSECKEYCMD_IDX(key_index);
171 if (is_gtk)
172 setkeycmd |= WUSBSETSECKEYCMD_GTK;
173
174 le_writel(tkid, whc->base + WUSBTKID);
175 for (i = 0; i < 4; i++)
176 le_writel(seckey[i], whc->base + WUSBSECKEY + 4*i);
177 le_writel(setkeycmd, whc->base + WUSBSETSECKEYCMD);
178
179 ret = whci_wait_for(&whc->umc->dev, whc->base + WUSBSETSECKEYCMD,
180 WUSBSETSECKEYCMD_SET, 0, 100, "set key");
181
182 return ret;
183}
184
185/**
186 * whc_set_ptk - set the PTK to use for a device.
187 *
188 * The index into the key table for this PTK is the same as the
189 * device's port index.
190 */
191int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
192 const void *ptk, size_t key_size)
193{
194 struct whc *whc = wusbhc_to_whc(wusbhc);
195 struct di_buf_entry *di = &whc->di_buf[port_idx];
196 int ret;
197
198 mutex_lock(&whc->mutex);
199
200 if (ptk) {
201 ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false);
202 if (ret)
203 goto out;
204
205 di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK;
206 di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx);
207 } else
208 di->addr_sec_info &= ~WHC_DI_SECURE;
209
210 ret = whc_update_di(whc, port_idx);
211out:
212 mutex_unlock(&whc->mutex);
213 return ret;
214}
215
216/**
217 * whc_set_gtk - set the GTK for subsequent broadcast packets
218 *
219 * The GTK is stored in the last entry in the key table (the previous
220 * N_DEVICES entries are for the per-device PTKs).
221 */
222int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
223 const void *gtk, size_t key_size)
224{
225 struct whc *whc = wusbhc_to_whc(wusbhc);
226 int ret;
227
228 mutex_lock(&whc->mutex);
229
230 ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true);
231
232 mutex_unlock(&whc->mutex);
233
234 return ret;
235}
236
237int whc_set_cluster_id(struct whc *whc, u8 bcid)
238{
239 whc_write_wusbcmd(whc, WUSBCMD_BCID_MASK, WUSBCMD_BCID(bcid));
240 return 0;
241}