diff options
author | Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 2008-09-17 11:34:23 -0400 |
---|---|---|
committer | David Vrabel <dv02@dv02pc01.europe.root.pri> | 2008-09-17 11:54:29 -0400 |
commit | 90ff96f22426a9d1a06df97dead0a9098facb567 (patch) | |
tree | bec977c054c6a1c5c05ae3f6dc3727b05194fa4e | |
parent | c7f736484f8ecde4dc1bc8459179c4d65f2ccbe4 (diff) |
wusb: add the Wireless USB core
Add support for Ceritified Wireless USB 1.0 to the USB stack.
This has been split into several patches for easier review.
core (this patch):
- host controller infrastructure
- cluster reservation
- UWB PAL registration
- fake root hub
protocol:
- MMC management (start/stop, managing IEs)
- device connection
security:
- device authentication and authorization
build-system:
- Kconfig and Kbuild files
Signed-off-by: David Vrabel <david.vrabel@csr.com>
-rw-r--r-- | drivers/usb/wusbcore/dev-sysfs.c | 143 | ||||
-rw-r--r-- | drivers/usb/wusbcore/pal.c | 39 | ||||
-rw-r--r-- | drivers/usb/wusbcore/reservation.c | 115 | ||||
-rw-r--r-- | drivers/usb/wusbcore/rh.c | 477 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wusbhc.c | 416 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wusbhc.h | 495 |
6 files changed, 1685 insertions, 0 deletions
diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/usb/wusbcore/dev-sysfs.c new file mode 100644 index 000000000000..7897a19652e5 --- /dev/null +++ b/drivers/usb/wusbcore/dev-sysfs.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * WUSB devices | ||
3 | * sysfs bindings | ||
4 | * | ||
5 | * Copyright (C) 2007 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 | * Get them out of the way... | ||
24 | */ | ||
25 | |||
26 | #include <linux/jiffies.h> | ||
27 | #include <linux/ctype.h> | ||
28 | #include <linux/workqueue.h> | ||
29 | #include "wusbhc.h" | ||
30 | |||
31 | #undef D_LOCAL | ||
32 | #define D_LOCAL 4 | ||
33 | #include <linux/uwb/debug.h> | ||
34 | |||
35 | static ssize_t wusb_disconnect_store(struct device *dev, | ||
36 | struct device_attribute *attr, | ||
37 | const char *buf, size_t size) | ||
38 | { | ||
39 | struct usb_device *usb_dev; | ||
40 | struct wusbhc *wusbhc; | ||
41 | unsigned command; | ||
42 | u8 port_idx; | ||
43 | |||
44 | if (sscanf(buf, "%u", &command) != 1) | ||
45 | return -EINVAL; | ||
46 | if (command == 0) | ||
47 | return size; | ||
48 | usb_dev = to_usb_device(dev); | ||
49 | wusbhc = wusbhc_get_by_usb_dev(usb_dev); | ||
50 | if (wusbhc == NULL) | ||
51 | return -ENODEV; | ||
52 | |||
53 | mutex_lock(&wusbhc->mutex); | ||
54 | port_idx = wusb_port_no_to_idx(usb_dev->portnum); | ||
55 | __wusbhc_dev_disable(wusbhc, port_idx); | ||
56 | mutex_unlock(&wusbhc->mutex); | ||
57 | wusbhc_put(wusbhc); | ||
58 | return size; | ||
59 | } | ||
60 | static DEVICE_ATTR(wusb_disconnect, 0200, NULL, wusb_disconnect_store); | ||
61 | |||
62 | static ssize_t wusb_cdid_show(struct device *dev, | ||
63 | struct device_attribute *attr, char *buf) | ||
64 | { | ||
65 | ssize_t result; | ||
66 | struct wusb_dev *wusb_dev; | ||
67 | |||
68 | wusb_dev = wusb_dev_get_by_usb_dev(to_usb_device(dev)); | ||
69 | if (wusb_dev == NULL) | ||
70 | return -ENODEV; | ||
71 | result = ckhdid_printf(buf, PAGE_SIZE, &wusb_dev->cdid); | ||
72 | strcat(buf, "\n"); | ||
73 | wusb_dev_put(wusb_dev); | ||
74 | return result + 1; | ||
75 | } | ||
76 | static DEVICE_ATTR(wusb_cdid, 0444, wusb_cdid_show, NULL); | ||
77 | |||
78 | static ssize_t wusb_ck_store(struct device *dev, | ||
79 | struct device_attribute *attr, | ||
80 | const char *buf, size_t size) | ||
81 | { | ||
82 | int result; | ||
83 | struct usb_device *usb_dev; | ||
84 | struct wusbhc *wusbhc; | ||
85 | struct wusb_ckhdid ck; | ||
86 | |||
87 | result = sscanf(buf, | ||
88 | "%02hhx %02hhx %02hhx %02hhx " | ||
89 | "%02hhx %02hhx %02hhx %02hhx " | ||
90 | "%02hhx %02hhx %02hhx %02hhx " | ||
91 | "%02hhx %02hhx %02hhx %02hhx\n", | ||
92 | &ck.data[0] , &ck.data[1], | ||
93 | &ck.data[2] , &ck.data[3], | ||
94 | &ck.data[4] , &ck.data[5], | ||
95 | &ck.data[6] , &ck.data[7], | ||
96 | &ck.data[8] , &ck.data[9], | ||
97 | &ck.data[10], &ck.data[11], | ||
98 | &ck.data[12], &ck.data[13], | ||
99 | &ck.data[14], &ck.data[15]); | ||
100 | if (result != 16) | ||
101 | return -EINVAL; | ||
102 | |||
103 | usb_dev = to_usb_device(dev); | ||
104 | wusbhc = wusbhc_get_by_usb_dev(usb_dev); | ||
105 | if (wusbhc == NULL) | ||
106 | return -ENODEV; | ||
107 | result = wusb_dev_4way_handshake(wusbhc, usb_dev->wusb_dev, &ck); | ||
108 | memset(&ck, 0, sizeof(ck)); | ||
109 | wusbhc_put(wusbhc); | ||
110 | return result < 0 ? result : size; | ||
111 | } | ||
112 | static DEVICE_ATTR(wusb_ck, 0200, NULL, wusb_ck_store); | ||
113 | |||
114 | static struct attribute *wusb_dev_attrs[] = { | ||
115 | &dev_attr_wusb_disconnect.attr, | ||
116 | &dev_attr_wusb_cdid.attr, | ||
117 | &dev_attr_wusb_ck.attr, | ||
118 | NULL, | ||
119 | }; | ||
120 | |||
121 | static struct attribute_group wusb_dev_attr_group = { | ||
122 | .name = NULL, /* we want them in the same directory */ | ||
123 | .attrs = wusb_dev_attrs, | ||
124 | }; | ||
125 | |||
126 | int wusb_dev_sysfs_add(struct wusbhc *wusbhc, struct usb_device *usb_dev, | ||
127 | struct wusb_dev *wusb_dev) | ||
128 | { | ||
129 | int result = sysfs_create_group(&usb_dev->dev.kobj, | ||
130 | &wusb_dev_attr_group); | ||
131 | struct device *dev = &usb_dev->dev; | ||
132 | if (result < 0) | ||
133 | dev_err(dev, "Cannot register WUSB-dev attributes: %d\n", | ||
134 | result); | ||
135 | return result; | ||
136 | } | ||
137 | |||
138 | void wusb_dev_sysfs_rm(struct wusb_dev *wusb_dev) | ||
139 | { | ||
140 | struct usb_device *usb_dev = wusb_dev->usb_dev; | ||
141 | if (usb_dev) | ||
142 | sysfs_remove_group(&usb_dev->dev.kobj, &wusb_dev_attr_group); | ||
143 | } | ||
diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c new file mode 100644 index 000000000000..cc126b444734 --- /dev/null +++ b/drivers/usb/wusbcore/pal.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * Wireless USB Host Controller | ||
3 | * UWB Protocol Adaptation Layer (PAL) glue. | ||
4 | * | ||
5 | * Copyright (C) 2008 Cambridge Silicon Radio Ltd. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version | ||
9 | * 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | #include "wusbhc.h" | ||
20 | |||
21 | /** | ||
22 | * wusbhc_pal_register - register the WUSB HC as a UWB PAL | ||
23 | * @wusbhc: the WUSB HC | ||
24 | */ | ||
25 | int wusbhc_pal_register(struct wusbhc *wusbhc) | ||
26 | { | ||
27 | uwb_pal_init(&wusbhc->pal); | ||
28 | |||
29 | return uwb_pal_register(wusbhc->uwb_rc, &wusbhc->pal); | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * wusbhc_pal_register - unregister the WUSB HC as a UWB PAL | ||
34 | * @wusbhc: the WUSB HC | ||
35 | */ | ||
36 | void wusbhc_pal_unregister(struct wusbhc *wusbhc) | ||
37 | { | ||
38 | uwb_pal_unregister(wusbhc->uwb_rc, &wusbhc->pal); | ||
39 | } | ||
diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c new file mode 100644 index 000000000000..fc63e77ded2d --- /dev/null +++ b/drivers/usb/wusbcore/reservation.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * WUSB cluster reservation management | ||
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/kernel.h> | ||
19 | #include <linux/uwb.h> | ||
20 | |||
21 | #include "wusbhc.h" | ||
22 | |||
23 | /* | ||
24 | * WUSB cluster reservations are multicast reservations with the | ||
25 | * broadcast cluster ID (BCID) as the target DevAddr. | ||
26 | * | ||
27 | * FIXME: consider adjusting the reservation depending on what devices | ||
28 | * are attached. | ||
29 | */ | ||
30 | |||
31 | static int wusbhc_bwa_set(struct wusbhc *wusbhc, u8 stream, | ||
32 | const struct uwb_mas_bm *mas) | ||
33 | { | ||
34 | if (mas == NULL) | ||
35 | mas = &uwb_mas_bm_zero; | ||
36 | return wusbhc->bwa_set(wusbhc, stream, mas); | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * wusbhc_rsv_complete_cb - WUSB HC reservation complete callback | ||
41 | * @rsv: the reservation | ||
42 | * | ||
43 | * Either set or clear the HC's view of the reservation. | ||
44 | * | ||
45 | * FIXME: when a reservation is denied the HC should be stopped. | ||
46 | */ | ||
47 | static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv) | ||
48 | { | ||
49 | struct wusbhc *wusbhc = rsv->pal_priv; | ||
50 | struct device *dev = wusbhc->dev; | ||
51 | char buf[72]; | ||
52 | |||
53 | switch (rsv->state) { | ||
54 | case UWB_RSV_STATE_O_ESTABLISHED: | ||
55 | bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS); | ||
56 | dev_dbg(dev, "established reservation: %s\n", buf); | ||
57 | wusbhc_bwa_set(wusbhc, rsv->stream, &rsv->mas); | ||
58 | break; | ||
59 | case UWB_RSV_STATE_NONE: | ||
60 | dev_dbg(dev, "removed reservation\n"); | ||
61 | wusbhc_bwa_set(wusbhc, 0, NULL); | ||
62 | wusbhc->rsv = NULL; | ||
63 | break; | ||
64 | default: | ||
65 | dev_dbg(dev, "unexpected reservation state: %d\n", rsv->state); | ||
66 | break; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | |||
71 | /** | ||
72 | * wusbhc_rsv_establish - establish a reservation for the cluster | ||
73 | * @wusbhc: the WUSB HC requesting a bandwith reservation | ||
74 | */ | ||
75 | int wusbhc_rsv_establish(struct wusbhc *wusbhc) | ||
76 | { | ||
77 | struct uwb_rc *rc = wusbhc->uwb_rc; | ||
78 | struct uwb_rsv *rsv; | ||
79 | struct uwb_dev_addr bcid; | ||
80 | int ret; | ||
81 | |||
82 | rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc); | ||
83 | if (rsv == NULL) | ||
84 | return -ENOMEM; | ||
85 | |||
86 | bcid.data[0] = wusbhc->cluster_id; | ||
87 | bcid.data[1] = 0; | ||
88 | |||
89 | rsv->owner = &rc->uwb_dev; | ||
90 | rsv->target.type = UWB_RSV_TARGET_DEVADDR; | ||
91 | rsv->target.devaddr = bcid; | ||
92 | rsv->type = UWB_DRP_TYPE_PRIVATE; | ||
93 | rsv->max_mas = 256; | ||
94 | rsv->min_mas = 16; /* one MAS per zone? */ | ||
95 | rsv->sparsity = 16; /* at least one MAS in each zone? */ | ||
96 | rsv->is_multicast = true; | ||
97 | |||
98 | ret = uwb_rsv_establish(rsv); | ||
99 | if (ret == 0) | ||
100 | wusbhc->rsv = rsv; | ||
101 | else | ||
102 | uwb_rsv_destroy(rsv); | ||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | |||
107 | /** | ||
108 | * wusbhc_rsv_terminate - terminate any cluster reservation | ||
109 | * @wusbhc: the WUSB host whose reservation is to be terminated | ||
110 | */ | ||
111 | void wusbhc_rsv_terminate(struct wusbhc *wusbhc) | ||
112 | { | ||
113 | if (wusbhc->rsv) | ||
114 | uwb_rsv_terminate(wusbhc->rsv); | ||
115 | } | ||
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c new file mode 100644 index 000000000000..267a64325106 --- /dev/null +++ b/drivers/usb/wusbcore/rh.c | |||
@@ -0,0 +1,477 @@ | |||
1 | /* | ||
2 | * Wireless USB Host Controller | ||
3 | * Root Hub operations | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2005-2006 Intel Corporation | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | * | ||
23 | * | ||
24 | * We fake a root hub that has fake ports (as many as simultaneous | ||
25 | * devices the Wireless USB Host Controller can deal with). For each | ||
26 | * port we keep an state in @wusbhc->port[index] identical to the one | ||
27 | * specified in the USB2.0[ch11] spec and some extra device | ||
28 | * information that complements the one in 'struct usb_device' (as | ||
29 | * this lacs a hcpriv pointer). | ||
30 | * | ||
31 | * Note this is common to WHCI and HWA host controllers. | ||
32 | * | ||
33 | * Through here we enable most of the state changes that the USB stack | ||
34 | * will use to connect or disconnect devices. We need to do some | ||
35 | * forced adaptation of Wireless USB device states vs. wired: | ||
36 | * | ||
37 | * USB: WUSB: | ||
38 | * | ||
39 | * Port Powered-off port slot n/a | ||
40 | * Powered-on port slot available | ||
41 | * Disconnected port slot available | ||
42 | * Connected port slot assigned device | ||
43 | * device sent DN_Connect | ||
44 | * device was authenticated | ||
45 | * Enabled device is authenticated, transitioned | ||
46 | * from unauth -> auth -> default address | ||
47 | * -> enabled | ||
48 | * Reset disconnect | ||
49 | * Disable disconnect | ||
50 | * | ||
51 | * This maps the standard USB port states with the WUSB device states | ||
52 | * so we can fake ports without having to modify the USB stack. | ||
53 | * | ||
54 | * FIXME: this process will change in the future | ||
55 | * | ||
56 | * | ||
57 | * ENTRY POINTS | ||
58 | * | ||
59 | * Our entry points into here are, as in hcd.c, the USB stack root hub | ||
60 | * ops defined in the usb_hcd struct: | ||
61 | * | ||
62 | * wusbhc_rh_status_data() Provide hub and port status data bitmap | ||
63 | * | ||
64 | * wusbhc_rh_control() Execution of all the major requests | ||
65 | * you can do to a hub (Set|Clear | ||
66 | * features, get descriptors, status, etc). | ||
67 | * | ||
68 | * wusbhc_rh_[suspend|resume]() That | ||
69 | * | ||
70 | * wusbhc_rh_start_port_reset() ??? unimplemented | ||
71 | */ | ||
72 | #include "wusbhc.h" | ||
73 | |||
74 | #define D_LOCAL 0 | ||
75 | #include <linux/uwb/debug.h> | ||
76 | |||
77 | /* | ||
78 | * Reset a fake port | ||
79 | * | ||
80 | * This can be called to reset a port from any other state or to reset | ||
81 | * it when connecting. In Wireless USB they are different; when doing | ||
82 | * a new connect that involves going over the authentication. When | ||
83 | * just reseting, its a different story. | ||
84 | * | ||
85 | * The Linux USB stack resets a port twice before it considers it | ||
86 | * enabled, so we have to detect and ignore that. | ||
87 | * | ||
88 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
89 | * | ||
90 | * Supposedly we are the only thread accesing @wusbhc->port; in any | ||
91 | * case, maybe we should move the mutex locking from | ||
92 | * wusbhc_devconnect_auth() to here. | ||
93 | * | ||
94 | * @port_idx refers to the wusbhc's port index, not the USB port number | ||
95 | */ | ||
96 | static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) | ||
97 | { | ||
98 | int result = 0; | ||
99 | struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); | ||
100 | |||
101 | d_fnstart(3, wusbhc->dev, "(wusbhc %p port_idx %u)\n", | ||
102 | wusbhc, port_idx); | ||
103 | if (port->reset_count == 0) { | ||
104 | wusbhc_devconnect_auth(wusbhc, port_idx); | ||
105 | port->reset_count++; | ||
106 | } else if (port->reset_count == 1) | ||
107 | /* see header */ | ||
108 | d_printf(2, wusbhc->dev, "Ignoring second reset on port_idx " | ||
109 | "%u\n", port_idx); | ||
110 | else | ||
111 | result = wusbhc_dev_reset(wusbhc, port_idx); | ||
112 | d_fnend(3, wusbhc->dev, "(wusbhc %p port_idx %u) = %d\n", | ||
113 | wusbhc, port_idx, result); | ||
114 | return result; | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * Return the hub change status bitmap | ||
119 | * | ||
120 | * The bits in the change status bitmap are cleared when a | ||
121 | * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4]. | ||
122 | * | ||
123 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
124 | * | ||
125 | * WARNING!! This gets called from atomic context; we cannot get the | ||
126 | * mutex--the only race condition we can find is some bit | ||
127 | * changing just after we copy it, which shouldn't be too | ||
128 | * big of a problem [and we can't make it an spinlock | ||
129 | * because other parts need to take it and sleep] . | ||
130 | * | ||
131 | * @usb_hcd is refcounted, so it won't dissapear under us | ||
132 | * and before killing a host, the polling of the root hub | ||
133 | * would be stopped anyway. | ||
134 | */ | ||
135 | int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf) | ||
136 | { | ||
137 | struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); | ||
138 | size_t cnt, size; | ||
139 | unsigned long *buf = (unsigned long *) _buf; | ||
140 | |||
141 | d_fnstart(1, wusbhc->dev, "(wusbhc %p)\n", wusbhc); | ||
142 | /* WE DON'T LOCK, see comment */ | ||
143 | size = wusbhc->ports_max + 1 /* hub bit */; | ||
144 | size = (size + 8 - 1) / 8; /* round to bytes */ | ||
145 | for (cnt = 0; cnt < wusbhc->ports_max; cnt++) | ||
146 | if (wusb_port_by_idx(wusbhc, cnt)->change) | ||
147 | set_bit(cnt + 1, buf); | ||
148 | else | ||
149 | clear_bit(cnt + 1, buf); | ||
150 | d_fnend(1, wusbhc->dev, "(wusbhc %p) %u, buffer:\n", wusbhc, (int)size); | ||
151 | d_dump(1, wusbhc->dev, _buf, size); | ||
152 | return size; | ||
153 | } | ||
154 | EXPORT_SYMBOL_GPL(wusbhc_rh_status_data); | ||
155 | |||
156 | /* | ||
157 | * Return the hub's desciptor | ||
158 | * | ||
159 | * NOTE: almost cut and paste from ehci-hub.c | ||
160 | * | ||
161 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked | ||
162 | */ | ||
163 | static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue, | ||
164 | u16 wIndex, | ||
165 | struct usb_hub_descriptor *descr, | ||
166 | u16 wLength) | ||
167 | { | ||
168 | u16 temp = 1 + (wusbhc->ports_max / 8); | ||
169 | u8 length = 7 + 2 * temp; | ||
170 | |||
171 | if (wLength < length) | ||
172 | return -ENOSPC; | ||
173 | descr->bDescLength = 7 + 2 * temp; | ||
174 | descr->bDescriptorType = 0x29; /* HUB type */ | ||
175 | descr->bNbrPorts = wusbhc->ports_max; | ||
176 | descr->wHubCharacteristics = cpu_to_le16( | ||
177 | 0x00 /* All ports power at once */ | ||
178 | | 0x00 /* not part of compound device */ | ||
179 | | 0x10 /* No overcurrent protection */ | ||
180 | | 0x00 /* 8 FS think time FIXME ?? */ | ||
181 | | 0x00); /* No port indicators */ | ||
182 | descr->bPwrOn2PwrGood = 0; | ||
183 | descr->bHubContrCurrent = 0; | ||
184 | /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ | ||
185 | memset(&descr->bitmap[0], 0, temp); | ||
186 | memset(&descr->bitmap[temp], 0xff, temp); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Clear a hub feature | ||
192 | * | ||
193 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
194 | * | ||
195 | * Nothing to do, so no locking needed ;) | ||
196 | */ | ||
197 | static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature) | ||
198 | { | ||
199 | int result; | ||
200 | struct device *dev = wusbhc->dev; | ||
201 | |||
202 | d_fnstart(4, dev, "(%p, feature 0x%04u)\n", wusbhc, feature); | ||
203 | switch (feature) { | ||
204 | case C_HUB_LOCAL_POWER: | ||
205 | /* FIXME: maybe plug bit 0 to the power input status, | ||
206 | * if any? | ||
207 | * see wusbhc_rh_get_hub_status() */ | ||
208 | case C_HUB_OVER_CURRENT: | ||
209 | result = 0; | ||
210 | break; | ||
211 | default: | ||
212 | result = -EPIPE; | ||
213 | } | ||
214 | d_fnend(4, dev, "(%p, feature 0x%04u), %d\n", wusbhc, feature, result); | ||
215 | return result; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * Return hub status (it is always zero...) | ||
220 | * | ||
221 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
222 | * | ||
223 | * Nothing to do, so no locking needed ;) | ||
224 | */ | ||
225 | static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf, | ||
226 | u16 wLength) | ||
227 | { | ||
228 | /* FIXME: maybe plug bit 0 to the power input status (if any)? */ | ||
229 | *buf = 0; | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * Set a port feature | ||
235 | * | ||
236 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
237 | */ | ||
238 | static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature, | ||
239 | u8 selector, u8 port_idx) | ||
240 | { | ||
241 | int result = -EINVAL; | ||
242 | struct device *dev = wusbhc->dev; | ||
243 | |||
244 | d_fnstart(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d)\n", | ||
245 | feature, selector, port_idx); | ||
246 | |||
247 | if (port_idx > wusbhc->ports_max) | ||
248 | goto error; | ||
249 | |||
250 | switch (feature) { | ||
251 | /* According to USB2.0[11.24.2.13]p2, these features | ||
252 | * are not required to be implemented. */ | ||
253 | case USB_PORT_FEAT_C_OVER_CURRENT: | ||
254 | case USB_PORT_FEAT_C_ENABLE: | ||
255 | case USB_PORT_FEAT_C_SUSPEND: | ||
256 | case USB_PORT_FEAT_C_CONNECTION: | ||
257 | case USB_PORT_FEAT_C_RESET: | ||
258 | result = 0; | ||
259 | break; | ||
260 | |||
261 | case USB_PORT_FEAT_POWER: | ||
262 | /* No such thing, but we fake it works */ | ||
263 | mutex_lock(&wusbhc->mutex); | ||
264 | wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER; | ||
265 | mutex_unlock(&wusbhc->mutex); | ||
266 | result = 0; | ||
267 | break; | ||
268 | case USB_PORT_FEAT_RESET: | ||
269 | result = wusbhc_rh_port_reset(wusbhc, port_idx); | ||
270 | break; | ||
271 | case USB_PORT_FEAT_ENABLE: | ||
272 | case USB_PORT_FEAT_SUSPEND: | ||
273 | dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n", | ||
274 | port_idx, feature, selector); | ||
275 | result = -ENOSYS; | ||
276 | break; | ||
277 | default: | ||
278 | dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n", | ||
279 | port_idx, feature, selector); | ||
280 | result = -EPIPE; | ||
281 | break; | ||
282 | } | ||
283 | error: | ||
284 | d_fnend(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d) = %d\n", | ||
285 | feature, selector, port_idx, result); | ||
286 | return result; | ||
287 | } | ||
288 | |||
289 | /* | ||
290 | * Clear a port feature... | ||
291 | * | ||
292 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
293 | */ | ||
294 | static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature, | ||
295 | u8 selector, u8 port_idx) | ||
296 | { | ||
297 | int result = -EINVAL; | ||
298 | struct device *dev = wusbhc->dev; | ||
299 | |||
300 | d_fnstart(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d)\n", | ||
301 | wusbhc, feature, selector, port_idx); | ||
302 | |||
303 | if (port_idx > wusbhc->ports_max) | ||
304 | goto error; | ||
305 | |||
306 | mutex_lock(&wusbhc->mutex); | ||
307 | result = 0; | ||
308 | switch (feature) { | ||
309 | case USB_PORT_FEAT_POWER: /* fake port always on */ | ||
310 | /* According to USB2.0[11.24.2.7.1.4], no need to implement? */ | ||
311 | case USB_PORT_FEAT_C_OVER_CURRENT: | ||
312 | break; | ||
313 | case USB_PORT_FEAT_C_RESET: | ||
314 | wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET; | ||
315 | break; | ||
316 | case USB_PORT_FEAT_C_CONNECTION: | ||
317 | wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION; | ||
318 | break; | ||
319 | case USB_PORT_FEAT_ENABLE: | ||
320 | __wusbhc_dev_disable(wusbhc, port_idx); | ||
321 | break; | ||
322 | case USB_PORT_FEAT_C_ENABLE: | ||
323 | wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE; | ||
324 | break; | ||
325 | case USB_PORT_FEAT_SUSPEND: | ||
326 | case USB_PORT_FEAT_C_SUSPEND: | ||
327 | case 0xffff: /* ??? FIXME */ | ||
328 | dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n", | ||
329 | port_idx, feature, selector); | ||
330 | /* dump_stack(); */ | ||
331 | result = -ENOSYS; | ||
332 | break; | ||
333 | default: | ||
334 | dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n", | ||
335 | port_idx, feature, selector); | ||
336 | result = -EPIPE; | ||
337 | break; | ||
338 | } | ||
339 | mutex_unlock(&wusbhc->mutex); | ||
340 | error: | ||
341 | d_fnend(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d) = " | ||
342 | "%d\n", wusbhc, feature, selector, port_idx, result); | ||
343 | return result; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Return the port's status | ||
348 | * | ||
349 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
350 | */ | ||
351 | static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, | ||
352 | u32 *_buf, u16 wLength) | ||
353 | { | ||
354 | int result = -EINVAL; | ||
355 | u16 *buf = (u16 *) _buf; | ||
356 | |||
357 | d_fnstart(1, wusbhc->dev, "(wusbhc %p port_idx %u wLength %u)\n", | ||
358 | wusbhc, port_idx, wLength); | ||
359 | if (port_idx > wusbhc->ports_max) | ||
360 | goto error; | ||
361 | mutex_lock(&wusbhc->mutex); | ||
362 | buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status); | ||
363 | buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change); | ||
364 | result = 0; | ||
365 | mutex_unlock(&wusbhc->mutex); | ||
366 | error: | ||
367 | d_fnend(1, wusbhc->dev, "(wusbhc %p) = %d, buffer:\n", wusbhc, result); | ||
368 | d_dump(1, wusbhc->dev, _buf, wLength); | ||
369 | return result; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * Entry point for Root Hub operations | ||
374 | * | ||
375 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | ||
376 | */ | ||
377 | int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue, | ||
378 | u16 wIndex, char *buf, u16 wLength) | ||
379 | { | ||
380 | int result = -ENOSYS; | ||
381 | struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); | ||
382 | |||
383 | switch (reqntype) { | ||
384 | case GetHubDescriptor: | ||
385 | result = wusbhc_rh_get_hub_descr( | ||
386 | wusbhc, wValue, wIndex, | ||
387 | (struct usb_hub_descriptor *) buf, wLength); | ||
388 | break; | ||
389 | case ClearHubFeature: | ||
390 | result = wusbhc_rh_clear_hub_feat(wusbhc, wValue); | ||
391 | break; | ||
392 | case GetHubStatus: | ||
393 | result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength); | ||
394 | break; | ||
395 | |||
396 | case SetPortFeature: | ||
397 | result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8, | ||
398 | (wIndex & 0xff) - 1); | ||
399 | break; | ||
400 | case ClearPortFeature: | ||
401 | result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8, | ||
402 | (wIndex & 0xff) - 1); | ||
403 | break; | ||
404 | case GetPortStatus: | ||
405 | result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1, | ||
406 | (u32 *)buf, wLength); | ||
407 | break; | ||
408 | |||
409 | case SetHubFeature: | ||
410 | default: | ||
411 | dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) " | ||
412 | "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype, | ||
413 | wValue, wIndex, buf, wLength); | ||
414 | /* dump_stack(); */ | ||
415 | result = -ENOSYS; | ||
416 | } | ||
417 | return result; | ||
418 | } | ||
419 | EXPORT_SYMBOL_GPL(wusbhc_rh_control); | ||
420 | |||
421 | int wusbhc_rh_suspend(struct usb_hcd *usb_hcd) | ||
422 | { | ||
423 | struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); | ||
424 | dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, | ||
425 | usb_hcd, wusbhc); | ||
426 | /* dump_stack(); */ | ||
427 | return -ENOSYS; | ||
428 | } | ||
429 | EXPORT_SYMBOL_GPL(wusbhc_rh_suspend); | ||
430 | |||
431 | int wusbhc_rh_resume(struct usb_hcd *usb_hcd) | ||
432 | { | ||
433 | struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); | ||
434 | dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, | ||
435 | usb_hcd, wusbhc); | ||
436 | /* dump_stack(); */ | ||
437 | return -ENOSYS; | ||
438 | } | ||
439 | EXPORT_SYMBOL_GPL(wusbhc_rh_resume); | ||
440 | |||
441 | int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx) | ||
442 | { | ||
443 | struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); | ||
444 | dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n", | ||
445 | __func__, usb_hcd, wusbhc, port_idx); | ||
446 | WARN_ON(1); | ||
447 | return -ENOSYS; | ||
448 | } | ||
449 | EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset); | ||
450 | |||
451 | static void wusb_port_init(struct wusb_port *port) | ||
452 | { | ||
453 | port->status |= USB_PORT_STAT_HIGH_SPEED; | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * Alloc fake port specific fields and status. | ||
458 | */ | ||
459 | int wusbhc_rh_create(struct wusbhc *wusbhc) | ||
460 | { | ||
461 | int result = -ENOMEM; | ||
462 | size_t port_size, itr; | ||
463 | port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]); | ||
464 | wusbhc->port = kzalloc(port_size, GFP_KERNEL); | ||
465 | if (wusbhc->port == NULL) | ||
466 | goto error_port_alloc; | ||
467 | for (itr = 0; itr < wusbhc->ports_max; itr++) | ||
468 | wusb_port_init(&wusbhc->port[itr]); | ||
469 | result = 0; | ||
470 | error_port_alloc: | ||
471 | return result; | ||
472 | } | ||
473 | |||
474 | void wusbhc_rh_destroy(struct wusbhc *wusbhc) | ||
475 | { | ||
476 | kfree(wusbhc->port); | ||
477 | } | ||
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c new file mode 100644 index 000000000000..1149b1e59c86 --- /dev/null +++ b/drivers/usb/wusbcore/wusbhc.c | |||
@@ -0,0 +1,416 @@ | |||
1 | /* | ||
2 | * Wireless USB Host Controller | ||
3 | * sysfs glue, wusbcore module support and life cycle management | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2005-2006 Intel Corporation | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | * | ||
23 | * | ||
24 | * Creation/destruction of wusbhc is split in two parts; that that | ||
25 | * doesn't require the HCD to be added (wusbhc_{create,destroy}) and | ||
26 | * the one that requires (phase B, wusbhc_b_{create,destroy}). | ||
27 | * | ||
28 | * This is so because usb_add_hcd() will start the HC, and thus, all | ||
29 | * the HC specific stuff has to be already initialiazed (like sysfs | ||
30 | * thingies). | ||
31 | */ | ||
32 | #include <linux/device.h> | ||
33 | #include <linux/module.h> | ||
34 | #include "wusbhc.h" | ||
35 | |||
36 | /** | ||
37 | * Extract the wusbhc that corresponds to a USB Host Controller class device | ||
38 | * | ||
39 | * WARNING! Apply only if @dev is that of a | ||
40 | * wusbhc.usb_hcd.self->class_dev; otherwise, you loose. | ||
41 | */ | ||
42 | static struct wusbhc *usbhc_dev_to_wusbhc(struct device *dev) | ||
43 | { | ||
44 | struct usb_bus *usb_bus = dev_get_drvdata(dev); | ||
45 | struct usb_hcd *usb_hcd = bus_to_hcd(usb_bus); | ||
46 | return usb_hcd_to_wusbhc(usb_hcd); | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * Show & store the current WUSB trust timeout | ||
51 | * | ||
52 | * We don't do locking--it is an 'atomic' value. | ||
53 | * | ||
54 | * The units that we store/show are always MILLISECONDS. However, the | ||
55 | * value of trust_timeout is jiffies. | ||
56 | */ | ||
57 | static ssize_t wusb_trust_timeout_show(struct device *dev, | ||
58 | struct device_attribute *attr, char *buf) | ||
59 | { | ||
60 | struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); | ||
61 | |||
62 | return scnprintf(buf, PAGE_SIZE, "%u\n", wusbhc->trust_timeout); | ||
63 | } | ||
64 | |||
65 | static ssize_t wusb_trust_timeout_store(struct device *dev, | ||
66 | struct device_attribute *attr, | ||
67 | const char *buf, size_t size) | ||
68 | { | ||
69 | struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); | ||
70 | ssize_t result = -ENOSYS; | ||
71 | unsigned trust_timeout; | ||
72 | |||
73 | result = sscanf(buf, "%u", &trust_timeout); | ||
74 | if (result != 1) { | ||
75 | result = -EINVAL; | ||
76 | goto out; | ||
77 | } | ||
78 | /* FIXME: maybe we should check for range validity? */ | ||
79 | wusbhc->trust_timeout = trust_timeout; | ||
80 | cancel_delayed_work(&wusbhc->keep_alive_timer); | ||
81 | flush_workqueue(wusbd); | ||
82 | queue_delayed_work(wusbd, &wusbhc->keep_alive_timer, | ||
83 | (trust_timeout * CONFIG_HZ)/1000/2); | ||
84 | out: | ||
85 | return result < 0 ? result : size; | ||
86 | } | ||
87 | static DEVICE_ATTR(wusb_trust_timeout, 0644, wusb_trust_timeout_show, | ||
88 | wusb_trust_timeout_store); | ||
89 | |||
90 | /* | ||
91 | * Show & store the current WUSB CHID | ||
92 | */ | ||
93 | static ssize_t wusb_chid_show(struct device *dev, | ||
94 | struct device_attribute *attr, char *buf) | ||
95 | { | ||
96 | struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); | ||
97 | ssize_t result = 0; | ||
98 | |||
99 | if (wusbhc->wuie_host_info != NULL) | ||
100 | result += ckhdid_printf(buf, PAGE_SIZE, | ||
101 | &wusbhc->wuie_host_info->CHID); | ||
102 | return result; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Store a new CHID | ||
107 | * | ||
108 | * This will (FIXME) trigger many changes. | ||
109 | * | ||
110 | * - Send an all zeros CHID and it will stop the controller | ||
111 | * - Send a non-zero CHID and it will start it | ||
112 | * (unless it was started, it will just change the CHID, | ||
113 | * diconnecting all devices first). | ||
114 | * | ||
115 | * So first we scan the MMC we are sent and then we act on it. We | ||
116 | * read it in the same format as we print it, an ASCII string of 16 | ||
117 | * hex bytes. | ||
118 | * | ||
119 | * See wusbhc_chid_set() for more info. | ||
120 | */ | ||
121 | static ssize_t wusb_chid_store(struct device *dev, | ||
122 | struct device_attribute *attr, | ||
123 | const char *buf, size_t size) | ||
124 | { | ||
125 | struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); | ||
126 | struct wusb_ckhdid chid; | ||
127 | ssize_t result; | ||
128 | |||
129 | result = sscanf(buf, | ||
130 | "%02hhx %02hhx %02hhx %02hhx " | ||
131 | "%02hhx %02hhx %02hhx %02hhx " | ||
132 | "%02hhx %02hhx %02hhx %02hhx " | ||
133 | "%02hhx %02hhx %02hhx %02hhx\n", | ||
134 | &chid.data[0] , &chid.data[1] , | ||
135 | &chid.data[2] , &chid.data[3] , | ||
136 | &chid.data[4] , &chid.data[5] , | ||
137 | &chid.data[6] , &chid.data[7] , | ||
138 | &chid.data[8] , &chid.data[9] , | ||
139 | &chid.data[10], &chid.data[11], | ||
140 | &chid.data[12], &chid.data[13], | ||
141 | &chid.data[14], &chid.data[15]); | ||
142 | if (result != 16) { | ||
143 | dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits): " | ||
144 | "%d\n", (int)result); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | result = wusbhc_chid_set(wusbhc, &chid); | ||
148 | return result < 0 ? result : size; | ||
149 | } | ||
150 | static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store); | ||
151 | |||
152 | /* Group all the WUSBHC attributes */ | ||
153 | static struct attribute *wusbhc_attrs[] = { | ||
154 | &dev_attr_wusb_trust_timeout.attr, | ||
155 | &dev_attr_wusb_chid.attr, | ||
156 | NULL, | ||
157 | }; | ||
158 | |||
159 | static struct attribute_group wusbhc_attr_group = { | ||
160 | .name = NULL, /* we want them in the same directory */ | ||
161 | .attrs = wusbhc_attrs, | ||
162 | }; | ||
163 | |||
164 | /* | ||
165 | * Create a wusbhc instance | ||
166 | * | ||
167 | * NOTEs: | ||
168 | * | ||
169 | * - assumes *wusbhc has been zeroed and wusbhc->usb_hcd has been | ||
170 | * initialized but not added. | ||
171 | * | ||
172 | * - fill out ports_max, mmcies_max and mmcie_{add,rm} before calling. | ||
173 | * | ||
174 | * - fill out wusbhc->uwb_rc and refcount it before calling | ||
175 | * - fill out the wusbhc->sec_modes array | ||
176 | */ | ||
177 | int wusbhc_create(struct wusbhc *wusbhc) | ||
178 | { | ||
179 | int result = 0; | ||
180 | |||
181 | wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS; | ||
182 | mutex_init(&wusbhc->mutex); | ||
183 | result = wusbhc_mmcie_create(wusbhc); | ||
184 | if (result < 0) | ||
185 | goto error_mmcie_create; | ||
186 | result = wusbhc_devconnect_create(wusbhc); | ||
187 | if (result < 0) | ||
188 | goto error_devconnect_create; | ||
189 | result = wusbhc_rh_create(wusbhc); | ||
190 | if (result < 0) | ||
191 | goto error_rh_create; | ||
192 | result = wusbhc_sec_create(wusbhc); | ||
193 | if (result < 0) | ||
194 | goto error_sec_create; | ||
195 | result = wusbhc_pal_register(wusbhc); | ||
196 | if (result < 0) | ||
197 | goto error_pal_register; | ||
198 | return 0; | ||
199 | |||
200 | error_pal_register: | ||
201 | wusbhc_sec_destroy(wusbhc); | ||
202 | error_sec_create: | ||
203 | wusbhc_rh_destroy(wusbhc); | ||
204 | error_rh_create: | ||
205 | wusbhc_devconnect_destroy(wusbhc); | ||
206 | error_devconnect_create: | ||
207 | wusbhc_mmcie_destroy(wusbhc); | ||
208 | error_mmcie_create: | ||
209 | return result; | ||
210 | } | ||
211 | EXPORT_SYMBOL_GPL(wusbhc_create); | ||
212 | |||
213 | static inline struct kobject *wusbhc_kobj(struct wusbhc *wusbhc) | ||
214 | { | ||
215 | return &wusbhc->usb_hcd.self.controller->kobj; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * Phase B of a wusbhc instance creation | ||
220 | * | ||
221 | * Creates fields that depend on wusbhc->usb_hcd having been | ||
222 | * added. This is where we create the sysfs files in | ||
223 | * /sys/class/usb_host/usb_hostX/. | ||
224 | * | ||
225 | * NOTE: Assumes wusbhc->usb_hcd has been already added by the upper | ||
226 | * layer (hwahc or whci) | ||
227 | */ | ||
228 | int wusbhc_b_create(struct wusbhc *wusbhc) | ||
229 | { | ||
230 | int result = 0; | ||
231 | struct device *dev = wusbhc->usb_hcd.self.controller; | ||
232 | |||
233 | result = sysfs_create_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); | ||
234 | if (result < 0) { | ||
235 | dev_err(dev, "Cannot register WUSBHC attributes: %d\n", result); | ||
236 | goto error_create_attr_group; | ||
237 | } | ||
238 | /* Yep, I plan to add stuff here... */ | ||
239 | error_create_attr_group: | ||
240 | return result; | ||
241 | } | ||
242 | EXPORT_SYMBOL_GPL(wusbhc_b_create); | ||
243 | |||
244 | void wusbhc_b_destroy(struct wusbhc *wusbhc) | ||
245 | { | ||
246 | sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); | ||
247 | } | ||
248 | EXPORT_SYMBOL_GPL(wusbhc_b_destroy); | ||
249 | |||
250 | void wusbhc_destroy(struct wusbhc *wusbhc) | ||
251 | { | ||
252 | wusbhc_pal_unregister(wusbhc); | ||
253 | wusbhc_sec_destroy(wusbhc); | ||
254 | wusbhc_rh_destroy(wusbhc); | ||
255 | wusbhc_devconnect_destroy(wusbhc); | ||
256 | wusbhc_mmcie_destroy(wusbhc); | ||
257 | } | ||
258 | EXPORT_SYMBOL_GPL(wusbhc_destroy); | ||
259 | |||
260 | struct workqueue_struct *wusbd; | ||
261 | EXPORT_SYMBOL_GPL(wusbd); | ||
262 | |||
263 | /* | ||
264 | * WUSB Cluster ID allocation map | ||
265 | * | ||
266 | * Each WUSB bus in a channel is identified with a Cluster Id in the | ||
267 | * unauth address pace (WUSB1.0[4.3]). We take the range 0xe0 to 0xff | ||
268 | * (that's space for 31 WUSB controllers, as 0xff can't be taken). We | ||
269 | * start taking from 0xff, 0xfe, 0xfd... (hence the += or -= 0xff). | ||
270 | * | ||
271 | * For each one we taken, we pin it in the bitap | ||
272 | */ | ||
273 | #define CLUSTER_IDS 32 | ||
274 | static DECLARE_BITMAP(wusb_cluster_id_table, CLUSTER_IDS); | ||
275 | static DEFINE_SPINLOCK(wusb_cluster_ids_lock); | ||
276 | |||
277 | /* | ||
278 | * Get a WUSB Cluster ID | ||
279 | * | ||
280 | * Need to release with wusb_cluster_id_put() when done w/ it. | ||
281 | */ | ||
282 | /* FIXME: coordinate with the choose_addres() from the USB stack */ | ||
283 | /* we want to leave the top of the 128 range for cluster addresses and | ||
284 | * the bottom for device addresses (as we map them one on one with | ||
285 | * ports). */ | ||
286 | u8 wusb_cluster_id_get(void) | ||
287 | { | ||
288 | u8 id; | ||
289 | spin_lock(&wusb_cluster_ids_lock); | ||
290 | id = find_first_zero_bit(wusb_cluster_id_table, CLUSTER_IDS); | ||
291 | if (id > CLUSTER_IDS) { | ||
292 | id = 0; | ||
293 | goto out; | ||
294 | } | ||
295 | set_bit(id, wusb_cluster_id_table); | ||
296 | id = (u8) 0xff - id; | ||
297 | out: | ||
298 | spin_unlock(&wusb_cluster_ids_lock); | ||
299 | return id; | ||
300 | |||
301 | } | ||
302 | EXPORT_SYMBOL_GPL(wusb_cluster_id_get); | ||
303 | |||
304 | /* | ||
305 | * Release a WUSB Cluster ID | ||
306 | * | ||
307 | * Obtained it with wusb_cluster_id_get() | ||
308 | */ | ||
309 | void wusb_cluster_id_put(u8 id) | ||
310 | { | ||
311 | id = 0xff - id; | ||
312 | BUG_ON(id >= CLUSTER_IDS); | ||
313 | spin_lock(&wusb_cluster_ids_lock); | ||
314 | WARN_ON(!test_bit(id, wusb_cluster_id_table)); | ||
315 | clear_bit(id, wusb_cluster_id_table); | ||
316 | spin_unlock(&wusb_cluster_ids_lock); | ||
317 | } | ||
318 | EXPORT_SYMBOL_GPL(wusb_cluster_id_put); | ||
319 | |||
320 | /** | ||
321 | * wusbhc_giveback_urb - return an URB to the USB core | ||
322 | * @wusbhc: the host controller the URB is from. | ||
323 | * @urb: the URB. | ||
324 | * @status: the URB's status. | ||
325 | * | ||
326 | * Return an URB to the USB core doing some additional WUSB specific | ||
327 | * processing. | ||
328 | * | ||
329 | * - After a successful transfer, update the trust timeout timestamp | ||
330 | * for the WUSB device. | ||
331 | * | ||
332 | * - [WUSB] sections 4.13 and 7.5.1 specifies the stop retrasmittion | ||
333 | * condition for the WCONNECTACK_IE is that the host has observed | ||
334 | * the associated device responding to a control transfer. | ||
335 | */ | ||
336 | void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status) | ||
337 | { | ||
338 | struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev); | ||
339 | |||
340 | if (status == 0) { | ||
341 | wusb_dev->entry_ts = jiffies; | ||
342 | |||
343 | /* wusbhc_devconnect_acked() can't be called from from | ||
344 | atomic context so defer it to a work queue. */ | ||
345 | if (!list_empty(&wusb_dev->cack_node)) | ||
346 | queue_work(wusbd, &wusb_dev->devconnect_acked_work); | ||
347 | } | ||
348 | |||
349 | usb_hcd_giveback_urb(&wusbhc->usb_hcd, urb, status); | ||
350 | } | ||
351 | EXPORT_SYMBOL_GPL(wusbhc_giveback_urb); | ||
352 | |||
353 | /** | ||
354 | * wusbhc_reset_all - reset the HC hardware | ||
355 | * @wusbhc: the host controller to reset. | ||
356 | * | ||
357 | * Request a full hardware reset of the chip. This will also reset | ||
358 | * the radio controller and any other PALs. | ||
359 | */ | ||
360 | void wusbhc_reset_all(struct wusbhc *wusbhc) | ||
361 | { | ||
362 | uwb_rc_reset_all(wusbhc->uwb_rc); | ||
363 | } | ||
364 | EXPORT_SYMBOL_GPL(wusbhc_reset_all); | ||
365 | |||
366 | static struct notifier_block wusb_usb_notifier = { | ||
367 | .notifier_call = wusb_usb_ncb, | ||
368 | .priority = INT_MAX /* Need to be called first of all */ | ||
369 | }; | ||
370 | |||
371 | static int __init wusbcore_init(void) | ||
372 | { | ||
373 | int result; | ||
374 | result = wusb_crypto_init(); | ||
375 | if (result < 0) | ||
376 | goto error_crypto_init; | ||
377 | /* WQ is singlethread because we need to serialize notifications */ | ||
378 | wusbd = create_singlethread_workqueue("wusbd"); | ||
379 | if (wusbd == NULL) { | ||
380 | result = -ENOMEM; | ||
381 | printk(KERN_ERR "WUSB-core: Cannot create wusbd workqueue\n"); | ||
382 | goto error_wusbd_create; | ||
383 | } | ||
384 | usb_register_notify(&wusb_usb_notifier); | ||
385 | bitmap_zero(wusb_cluster_id_table, CLUSTER_IDS); | ||
386 | set_bit(0, wusb_cluster_id_table); /* reserve Cluster ID 0xff */ | ||
387 | return 0; | ||
388 | |||
389 | error_wusbd_create: | ||
390 | wusb_crypto_exit(); | ||
391 | error_crypto_init: | ||
392 | return result; | ||
393 | |||
394 | } | ||
395 | module_init(wusbcore_init); | ||
396 | |||
397 | static void __exit wusbcore_exit(void) | ||
398 | { | ||
399 | clear_bit(0, wusb_cluster_id_table); | ||
400 | if (!bitmap_empty(wusb_cluster_id_table, CLUSTER_IDS)) { | ||
401 | char buf[256]; | ||
402 | bitmap_scnprintf(buf, sizeof(buf), wusb_cluster_id_table, | ||
403 | CLUSTER_IDS); | ||
404 | printk(KERN_ERR "BUG: WUSB Cluster IDs not released " | ||
405 | "on exit: %s\n", buf); | ||
406 | WARN_ON(1); | ||
407 | } | ||
408 | usb_unregister_notify(&wusb_usb_notifier); | ||
409 | destroy_workqueue(wusbd); | ||
410 | wusb_crypto_exit(); | ||
411 | } | ||
412 | module_exit(wusbcore_exit); | ||
413 | |||
414 | MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); | ||
415 | MODULE_DESCRIPTION("Wireless USB core"); | ||
416 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h new file mode 100644 index 000000000000..d0c132434f1b --- /dev/null +++ b/drivers/usb/wusbcore/wusbhc.h | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * Wireless USB Host Controller | ||
3 | * Common infrastructure for WHCI and HWA WUSB-HC drivers | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2005-2006 Intel Corporation | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | * | ||
23 | * | ||
24 | * This driver implements parts common to all Wireless USB Host | ||
25 | * Controllers (struct wusbhc, embedding a struct usb_hcd) and is used | ||
26 | * by: | ||
27 | * | ||
28 | * - hwahc: HWA, USB-dongle that implements a Wireless USB host | ||
29 | * controller, (Wireless USB 1.0 Host-Wire-Adapter specification). | ||
30 | * | ||
31 | * - whci: WHCI, a PCI card with a wireless host controller | ||
32 | * (Wireless Host Controller Interface 1.0 specification). | ||
33 | * | ||
34 | * Check out the Design-overview.txt file in the source documentation | ||
35 | * for other details on the implementation. | ||
36 | * | ||
37 | * Main blocks: | ||
38 | * | ||
39 | * rh Root Hub emulation (part of the HCD glue) | ||
40 | * | ||
41 | * devconnect Handle all the issues related to device connection, | ||
42 | * authentication, disconnection, timeout, reseting, | ||
43 | * keepalives, etc. | ||
44 | * | ||
45 | * mmc MMC IE broadcasting handling | ||
46 | * | ||
47 | * A host controller driver just initializes its stuff and as part of | ||
48 | * that, creates a 'struct wusbhc' instance that handles all the | ||
49 | * common WUSB mechanisms. Links in the function ops that are specific | ||
50 | * to it and then registers the host controller. Ready to run. | ||
51 | */ | ||
52 | |||
53 | #ifndef __WUSBHC_H__ | ||
54 | #define __WUSBHC_H__ | ||
55 | |||
56 | #include <linux/usb.h> | ||
57 | #include <linux/list.h> | ||
58 | #include <linux/mutex.h> | ||
59 | #include <linux/kref.h> | ||
60 | #include <linux/workqueue.h> | ||
61 | /* FIXME: Yes, I know: BAD--it's not my fault the USB HC iface is not | ||
62 | * public */ | ||
63 | #include <linux/../../drivers/usb/core/hcd.h> | ||
64 | #include <linux/uwb.h> | ||
65 | #include <linux/usb/wusb.h> | ||
66 | |||
67 | |||
68 | /** | ||
69 | * Wireless USB device | ||
70 | * | ||
71 | * Describe a WUSB device connected to the cluster. This struct | ||
72 | * belongs to the 'struct wusb_port' it is attached to and it is | ||
73 | * responsible for putting and clearing the pointer to it. | ||
74 | * | ||
75 | * Note this "complements" the 'struct usb_device' that the usb_hcd | ||
76 | * keeps for each connected USB device. However, it extends some | ||
77 | * information that is not available (there is no hcpriv ptr in it!) | ||
78 | * *and* most importantly, it's life cycle is different. It is created | ||
79 | * as soon as we get a DN_Connect (connect request notification) from | ||
80 | * the device through the WUSB host controller; the USB stack doesn't | ||
81 | * create the device until we authenticate it. FIXME: this will | ||
82 | * change. | ||
83 | * | ||
84 | * @bos: This is allocated when the BOS descriptors are read from | ||
85 | * the device and freed upon the wusb_dev struct dying. | ||
86 | * @wusb_cap_descr: points into @bos, and has been verified to be size | ||
87 | * safe. | ||
88 | */ | ||
89 | struct wusb_dev { | ||
90 | struct kref refcnt; | ||
91 | struct wusbhc *wusbhc; | ||
92 | struct list_head cack_node; /* Connect-Ack list */ | ||
93 | u8 port_idx; | ||
94 | u8 addr; | ||
95 | u8 beacon_type:4; | ||
96 | struct usb_encryption_descriptor ccm1_etd; | ||
97 | struct wusb_ckhdid cdid; | ||
98 | unsigned long entry_ts; | ||
99 | struct usb_bos_descriptor *bos; | ||
100 | struct usb_wireless_cap_descriptor *wusb_cap_descr; | ||
101 | struct uwb_mas_bm availability; | ||
102 | struct work_struct devconnect_acked_work; | ||
103 | struct urb *set_gtk_urb; | ||
104 | struct usb_ctrlrequest *set_gtk_req; | ||
105 | struct usb_device *usb_dev; | ||
106 | }; | ||
107 | |||
108 | #define WUSB_DEV_ADDR_UNAUTH 0x80 | ||
109 | |||
110 | static inline void wusb_dev_init(struct wusb_dev *wusb_dev) | ||
111 | { | ||
112 | kref_init(&wusb_dev->refcnt); | ||
113 | /* no need to init the cack_node */ | ||
114 | } | ||
115 | |||
116 | extern void wusb_dev_destroy(struct kref *_wusb_dev); | ||
117 | |||
118 | static inline struct wusb_dev *wusb_dev_get(struct wusb_dev *wusb_dev) | ||
119 | { | ||
120 | kref_get(&wusb_dev->refcnt); | ||
121 | return wusb_dev; | ||
122 | } | ||
123 | |||
124 | static inline void wusb_dev_put(struct wusb_dev *wusb_dev) | ||
125 | { | ||
126 | kref_put(&wusb_dev->refcnt, wusb_dev_destroy); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * Wireless USB Host Controlller root hub "fake" ports | ||
131 | * (state and device information) | ||
132 | * | ||
133 | * Wireless USB is wireless, so there are no ports; but we | ||
134 | * fake'em. Each RC can connect a max of devices at the same time | ||
135 | * (given in the Wireless Adapter descriptor, bNumPorts or WHCI's | ||
136 | * caps), referred to in wusbhc->ports_max. | ||
137 | * | ||
138 | * See rh.c for more information. | ||
139 | * | ||
140 | * The @status and @change use the same bits as in USB2.0[11.24.2.7], | ||
141 | * so we don't have to do much when getting the port's status. | ||
142 | * | ||
143 | * WUSB1.0[7.1], USB2.0[11.24.2.7.1,fig 11-10], | ||
144 | * include/linux/usb_ch9.h (#define USB_PORT_STAT_*) | ||
145 | */ | ||
146 | struct wusb_port { | ||
147 | u16 status; | ||
148 | u16 change; | ||
149 | struct wusb_dev *wusb_dev; /* connected device's info */ | ||
150 | unsigned reset_count; | ||
151 | u32 ptk_tkid; | ||
152 | }; | ||
153 | |||
154 | /** | ||
155 | * WUSB Host Controller specifics | ||
156 | * | ||
157 | * All fields that are common to all Wireless USB controller types | ||
158 | * (HWA and WHCI) are grouped here. Host Controller | ||
159 | * functions/operations that only deal with general Wireless USB HC | ||
160 | * issues use this data type to refer to the host. | ||
161 | * | ||
162 | * @usb_hcd Instantiation of a USB host controller | ||
163 | * (initialized by upper layer [HWA=HC or WHCI]. | ||
164 | * | ||
165 | * @dev Device that implements this; initialized by the | ||
166 | * upper layer (HWA-HC, WHCI...); this device should | ||
167 | * have a refcount. | ||
168 | * | ||
169 | * @trust_timeout After this time without hearing for device | ||
170 | * activity, we consider the device gone and we have to | ||
171 | * re-authenticate. | ||
172 | * | ||
173 | * Can be accessed w/o locking--however, read to a | ||
174 | * local variable then use. | ||
175 | * | ||
176 | * @chid WUSB Cluster Host ID: this is supposed to be a | ||
177 | * unique value that doesn't change across reboots (so | ||
178 | * that your devices do not require re-association). | ||
179 | * | ||
180 | * Read/Write protected by @mutex | ||
181 | * | ||
182 | * @dev_info This array has ports_max elements. It is used to | ||
183 | * give the HC information about the WUSB devices (see | ||
184 | * 'struct wusb_dev_info'). | ||
185 | * | ||
186 | * For HWA we need to allocate it in heap; for WHCI it | ||
187 | * needs to be permanently mapped, so we keep it for | ||
188 | * both and make it easy. Call wusbhc->dev_info_set() | ||
189 | * to update an entry. | ||
190 | * | ||
191 | * @ports_max Number of simultaneous device connections (fake | ||
192 | * ports) this HC will take. Read-only. | ||
193 | * | ||
194 | * @port Array of port status for each fake root port. Guaranteed to | ||
195 | * always be the same lenght during device existence | ||
196 | * [this allows for some unlocked but referenced reading]. | ||
197 | * | ||
198 | * @mmcies_max Max number of Information Elements this HC can send | ||
199 | * in its MMC. Read-only. | ||
200 | * | ||
201 | * @mmcie_add HC specific operation (WHCI or HWA) for adding an | ||
202 | * MMCIE. | ||
203 | * | ||
204 | * @mmcie_rm HC specific operation (WHCI or HWA) for removing an | ||
205 | * MMCIE. | ||
206 | * | ||
207 | * @enc_types Array which describes the encryptions methods | ||
208 | * supported by the host as described in WUSB1.0 -- | ||
209 | * one entry per supported method. As of WUSB1.0 there | ||
210 | * is only four methods, we make space for eight just in | ||
211 | * case they decide to add some more (and pray they do | ||
212 | * it in sequential order). if 'enc_types[enc_method] | ||
213 | * != 0', then it is supported by the host. enc_method | ||
214 | * is USB_ENC_TYPE*. | ||
215 | * | ||
216 | * @set_ptk: Set the PTK and enable encryption for a device. Or, if | ||
217 | * the supplied key is NULL, disable encryption for that | ||
218 | * device. | ||
219 | * | ||
220 | * @set_gtk: Set the GTK to be used for all future broadcast packets | ||
221 | * (i.e., MMCs). With some hardware, setting the GTK may start | ||
222 | * MMC transmission. | ||
223 | * | ||
224 | * NOTE: | ||
225 | * | ||
226 | * - If wusb_dev->usb_dev is not NULL, then usb_dev is valid | ||
227 | * (wusb_dev has a refcount on it). Likewise, if usb_dev->wusb_dev | ||
228 | * is not NULL, usb_dev->wusb_dev is valid (usb_dev keeps a | ||
229 | * refcount on it). | ||
230 | * | ||
231 | * Most of the times when you need to use it, it will be non-NULL, | ||
232 | * so there is no real need to check for it (wusb_dev will | ||
233 | * dissapear before usb_dev). | ||
234 | * | ||
235 | * - The following fields need to be filled out before calling | ||
236 | * wusbhc_create(): ports_max, mmcies_max, mmcie_{add,rm}. | ||
237 | * | ||
238 | * - there is no wusbhc_init() method, we do everything in | ||
239 | * wusbhc_create(). | ||
240 | * | ||
241 | * - Creation is done in two phases, wusbhc_create() and | ||
242 | * wusbhc_create_b(); b are the parts that need to be called after | ||
243 | * calling usb_hcd_add(&wusbhc->usb_hcd). | ||
244 | */ | ||
245 | struct wusbhc { | ||
246 | struct usb_hcd usb_hcd; /* HAS TO BE 1st */ | ||
247 | struct device *dev; | ||
248 | struct uwb_rc *uwb_rc; | ||
249 | struct uwb_pal pal; | ||
250 | |||
251 | unsigned trust_timeout; /* in jiffies */ | ||
252 | struct wuie_host_info *wuie_host_info; /* Includes CHID */ | ||
253 | |||
254 | struct mutex mutex; /* locks everything else */ | ||
255 | u16 cluster_id; /* Wireless USB Cluster ID */ | ||
256 | struct wusb_port *port; /* Fake port status handling */ | ||
257 | struct wusb_dev_info *dev_info; /* for Set Device Info mgmt */ | ||
258 | u8 ports_max; | ||
259 | unsigned active:1; /* currently xmit'ing MMCs */ | ||
260 | struct wuie_keep_alive keep_alive_ie; /* protected by mutex */ | ||
261 | struct delayed_work keep_alive_timer; | ||
262 | struct list_head cack_list; /* Connect acknowledging */ | ||
263 | size_t cack_count; /* protected by 'mutex' */ | ||
264 | struct wuie_connect_ack cack_ie; | ||
265 | struct uwb_rsv *rsv; /* cluster bandwidth reservation */ | ||
266 | |||
267 | struct mutex mmcie_mutex; /* MMC WUIE handling */ | ||
268 | struct wuie_hdr **mmcie; /* WUIE array */ | ||
269 | u8 mmcies_max; | ||
270 | /* FIXME: make wusbhc_ops? */ | ||
271 | int (*start)(struct wusbhc *wusbhc); | ||
272 | void (*stop)(struct wusbhc *wusbhc); | ||
273 | int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, | ||
274 | u8 handle, struct wuie_hdr *wuie); | ||
275 | int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle); | ||
276 | int (*dev_info_set)(struct wusbhc *, struct wusb_dev *wusb_dev); | ||
277 | int (*bwa_set)(struct wusbhc *wusbhc, s8 stream_index, | ||
278 | const struct uwb_mas_bm *); | ||
279 | int (*set_ptk)(struct wusbhc *wusbhc, u8 port_idx, | ||
280 | u32 tkid, const void *key, size_t key_size); | ||
281 | int (*set_gtk)(struct wusbhc *wusbhc, | ||
282 | u32 tkid, const void *key, size_t key_size); | ||
283 | int (*set_num_dnts)(struct wusbhc *wusbhc, u8 interval, u8 slots); | ||
284 | |||
285 | struct { | ||
286 | struct usb_key_descriptor descr; | ||
287 | u8 data[16]; /* GTK key data */ | ||
288 | } __attribute__((packed)) gtk; | ||
289 | u8 gtk_index; | ||
290 | u32 gtk_tkid; | ||
291 | struct work_struct gtk_rekey_done_work; | ||
292 | int pending_set_gtks; | ||
293 | |||
294 | struct usb_encryption_descriptor *ccm1_etd; | ||
295 | }; | ||
296 | |||
297 | #define usb_hcd_to_wusbhc(u) container_of((u), struct wusbhc, usb_hcd) | ||
298 | |||
299 | |||
300 | extern int wusbhc_create(struct wusbhc *); | ||
301 | extern int wusbhc_b_create(struct wusbhc *); | ||
302 | extern void wusbhc_b_destroy(struct wusbhc *); | ||
303 | extern void wusbhc_destroy(struct wusbhc *); | ||
304 | extern int wusb_dev_sysfs_add(struct wusbhc *, struct usb_device *, | ||
305 | struct wusb_dev *); | ||
306 | extern void wusb_dev_sysfs_rm(struct wusb_dev *); | ||
307 | extern int wusbhc_sec_create(struct wusbhc *); | ||
308 | extern int wusbhc_sec_start(struct wusbhc *); | ||
309 | extern void wusbhc_sec_stop(struct wusbhc *); | ||
310 | extern void wusbhc_sec_destroy(struct wusbhc *); | ||
311 | extern void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, | ||
312 | int status); | ||
313 | void wusbhc_reset_all(struct wusbhc *wusbhc); | ||
314 | |||
315 | int wusbhc_pal_register(struct wusbhc *wusbhc); | ||
316 | void wusbhc_pal_unregister(struct wusbhc *wusbhc); | ||
317 | |||
318 | /* | ||
319 | * Return @usb_dev's @usb_hcd (properly referenced) or NULL if gone | ||
320 | * | ||
321 | * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr) | ||
322 | * | ||
323 | * This is a safe assumption as @usb_dev->bus is referenced all the | ||
324 | * time during the @usb_dev life cycle. | ||
325 | */ | ||
326 | static inline struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev) | ||
327 | { | ||
328 | struct usb_hcd *usb_hcd; | ||
329 | usb_hcd = container_of(usb_dev->bus, struct usb_hcd, self); | ||
330 | return usb_get_hcd(usb_hcd); | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Increment the reference count on a wusbhc. | ||
335 | * | ||
336 | * @wusbhc's life cycle is identical to that of the underlying usb_hcd. | ||
337 | */ | ||
338 | static inline struct wusbhc *wusbhc_get(struct wusbhc *wusbhc) | ||
339 | { | ||
340 | return usb_get_hcd(&wusbhc->usb_hcd) ? wusbhc : NULL; | ||
341 | } | ||
342 | |||
343 | /* | ||
344 | * Return the wusbhc associated to a @usb_dev | ||
345 | * | ||
346 | * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr) | ||
347 | * | ||
348 | * @returns: wusbhc for @usb_dev; NULL if the @usb_dev is being torn down. | ||
349 | * WARNING: referenced at the usb_hcd level, unlocked | ||
350 | * | ||
351 | * FIXME: move offline | ||
352 | */ | ||
353 | static inline struct wusbhc *wusbhc_get_by_usb_dev(struct usb_device *usb_dev) | ||
354 | { | ||
355 | struct wusbhc *wusbhc = NULL; | ||
356 | struct usb_hcd *usb_hcd; | ||
357 | if (usb_dev->devnum > 1 && !usb_dev->wusb) { | ||
358 | /* but root hubs */ | ||
359 | dev_err(&usb_dev->dev, "devnum %d wusb %d\n", usb_dev->devnum, | ||
360 | usb_dev->wusb); | ||
361 | BUG_ON(usb_dev->devnum > 1 && !usb_dev->wusb); | ||
362 | } | ||
363 | usb_hcd = usb_hcd_get_by_usb_dev(usb_dev); | ||
364 | if (usb_hcd == NULL) | ||
365 | return NULL; | ||
366 | BUG_ON(usb_hcd->wireless == 0); | ||
367 | return wusbhc = usb_hcd_to_wusbhc(usb_hcd); | ||
368 | } | ||
369 | |||
370 | |||
371 | static inline void wusbhc_put(struct wusbhc *wusbhc) | ||
372 | { | ||
373 | usb_put_hcd(&wusbhc->usb_hcd); | ||
374 | } | ||
375 | |||
376 | int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid); | ||
377 | void wusbhc_stop(struct wusbhc *wusbhc); | ||
378 | extern int wusbhc_chid_set(struct wusbhc *, const struct wusb_ckhdid *); | ||
379 | |||
380 | /* Device connect handling */ | ||
381 | extern int wusbhc_devconnect_create(struct wusbhc *); | ||
382 | extern void wusbhc_devconnect_destroy(struct wusbhc *); | ||
383 | extern int wusbhc_devconnect_start(struct wusbhc *wusbhc, | ||
384 | const struct wusb_ckhdid *chid); | ||
385 | extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc); | ||
386 | extern int wusbhc_devconnect_auth(struct wusbhc *, u8); | ||
387 | extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr, | ||
388 | struct wusb_dn_hdr *dn_hdr, size_t size); | ||
389 | extern int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port); | ||
390 | extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port); | ||
391 | extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, | ||
392 | void *priv); | ||
393 | extern int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, | ||
394 | u8 addr); | ||
395 | |||
396 | /* Wireless USB fake Root Hub methods */ | ||
397 | extern int wusbhc_rh_create(struct wusbhc *); | ||
398 | extern void wusbhc_rh_destroy(struct wusbhc *); | ||
399 | |||
400 | extern int wusbhc_rh_status_data(struct usb_hcd *, char *); | ||
401 | extern int wusbhc_rh_control(struct usb_hcd *, u16, u16, u16, char *, u16); | ||
402 | extern int wusbhc_rh_suspend(struct usb_hcd *); | ||
403 | extern int wusbhc_rh_resume(struct usb_hcd *); | ||
404 | extern int wusbhc_rh_start_port_reset(struct usb_hcd *, unsigned); | ||
405 | |||
406 | /* MMC handling */ | ||
407 | extern int wusbhc_mmcie_create(struct wusbhc *); | ||
408 | extern void wusbhc_mmcie_destroy(struct wusbhc *); | ||
409 | extern int wusbhc_mmcie_set(struct wusbhc *, u8 interval, u8 repeat_cnt, | ||
410 | struct wuie_hdr *); | ||
411 | extern void wusbhc_mmcie_rm(struct wusbhc *, struct wuie_hdr *); | ||
412 | |||
413 | /* Bandwidth reservation */ | ||
414 | int wusbhc_rsv_establish(struct wusbhc *wusbhc); | ||
415 | void wusbhc_rsv_terminate(struct wusbhc *wusbhc); | ||
416 | |||
417 | /* | ||
418 | * I've always said | ||
419 | * I wanted a wedding in a church... | ||
420 | * | ||
421 | * but lately I've been thinking about | ||
422 | * the Botanical Gardens. | ||
423 | * | ||
424 | * We could do it by the tulips. | ||
425 | * It'll be beautiful | ||
426 | * | ||
427 | * --Security! | ||
428 | */ | ||
429 | extern int wusb_dev_sec_add(struct wusbhc *, struct usb_device *, | ||
430 | struct wusb_dev *); | ||
431 | extern void wusb_dev_sec_rm(struct wusb_dev *) ; | ||
432 | extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *, | ||
433 | struct wusb_ckhdid *ck); | ||
434 | void wusbhc_gtk_rekey(struct wusbhc *wusbhc); | ||
435 | |||
436 | |||
437 | /* WUSB Cluster ID handling */ | ||
438 | extern u8 wusb_cluster_id_get(void); | ||
439 | extern void wusb_cluster_id_put(u8); | ||
440 | |||
441 | /* | ||
442 | * wusb_port_by_idx - return the port associated to a zero-based port index | ||
443 | * | ||
444 | * NOTE: valid without locking as long as wusbhc is referenced (as the | ||
445 | * number of ports doesn't change). The data pointed to has to | ||
446 | * be verified though :) | ||
447 | */ | ||
448 | static inline struct wusb_port *wusb_port_by_idx(struct wusbhc *wusbhc, | ||
449 | u8 port_idx) | ||
450 | { | ||
451 | return &wusbhc->port[port_idx]; | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * wusb_port_no_to_idx - Convert port number (per usb_dev->portnum) to | ||
456 | * a port_idx. | ||
457 | * | ||
458 | * USB stack USB ports are 1 based!! | ||
459 | * | ||
460 | * NOTE: only valid for WUSB devices!!! | ||
461 | */ | ||
462 | static inline u8 wusb_port_no_to_idx(u8 port_no) | ||
463 | { | ||
464 | return port_no - 1; | ||
465 | } | ||
466 | |||
467 | extern struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *, | ||
468 | struct usb_device *); | ||
469 | |||
470 | /* | ||
471 | * Return a referenced wusb_dev given a @usb_dev | ||
472 | * | ||
473 | * Returns NULL if the usb_dev is being torn down. | ||
474 | * | ||
475 | * FIXME: move offline | ||
476 | */ | ||
477 | static inline | ||
478 | struct wusb_dev *wusb_dev_get_by_usb_dev(struct usb_device *usb_dev) | ||
479 | { | ||
480 | struct wusbhc *wusbhc; | ||
481 | struct wusb_dev *wusb_dev; | ||
482 | wusbhc = wusbhc_get_by_usb_dev(usb_dev); | ||
483 | if (wusbhc == NULL) | ||
484 | return NULL; | ||
485 | mutex_lock(&wusbhc->mutex); | ||
486 | wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev); | ||
487 | mutex_unlock(&wusbhc->mutex); | ||
488 | wusbhc_put(wusbhc); | ||
489 | return wusb_dev; | ||
490 | } | ||
491 | |||
492 | /* Misc */ | ||
493 | |||
494 | extern struct workqueue_struct *wusbd; | ||
495 | #endif /* #ifndef __WUSBHC_H__ */ | ||