| @@ -1,1107 +1,1107 @@ | | | @@ -1,1107 +1,1107 @@ |
1 | /* $NetBSD: if_iwm.c,v 1.20 2015/03/03 09:27:35 nonaka Exp $ */ | | 1 | /* $NetBSD: if_iwm.c,v 1.21 2015/03/03 09:34:40 nonaka Exp $ */ |
2 | /* OpenBSD: if_iwm.c,v 1.18 2015/02/11 01:12:42 brad Exp */ | | 2 | /* OpenBSD: if_iwm.c,v 1.18 2015/02/11 01:12:42 brad Exp */ |
3 | | | 3 | |
4 | /* | | 4 | /* |
5 | * Copyright (c) 2014 genua mbh <info@genua.de> | | 5 | * Copyright (c) 2014 genua mbh <info@genua.de> |
6 | * Copyright (c) 2014 Fixup Software Ltd. | | 6 | * Copyright (c) 2014 Fixup Software Ltd. |
7 | * | | 7 | * |
8 | * Permission to use, copy, modify, and distribute this software for any | | 8 | * Permission to use, copy, modify, and distribute this software for any |
9 | * purpose with or without fee is hereby granted, provided that the above | | 9 | * purpose with or without fee is hereby granted, provided that the above |
10 | * copyright notice and this permission notice appear in all copies. | | 10 | * copyright notice and this permission notice appear in all copies. |
11 | * | | 11 | * |
12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 | */ | | 19 | */ |
20 | | | 20 | |
21 | /*- | | 21 | /*- |
22 | * Based on BSD-licensed source modules in the Linux iwlwifi driver, | | 22 | * Based on BSD-licensed source modules in the Linux iwlwifi driver, |
23 | * which were used as the reference documentation for this implementation. | | 23 | * which were used as the reference documentation for this implementation. |
24 | * | | 24 | * |
25 | * Driver version we are currently based off of is | | 25 | * Driver version we are currently based off of is |
26 | * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd) | | 26 | * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd) |
27 | * | | 27 | * |
28 | *********************************************************************** | | 28 | *********************************************************************** |
29 | * | | 29 | * |
30 | * This file is provided under a dual BSD/GPLv2 license. When using or | | 30 | * This file is provided under a dual BSD/GPLv2 license. When using or |
31 | * redistributing this file, you may do so under either license. | | 31 | * redistributing this file, you may do so under either license. |
32 | * | | 32 | * |
33 | * GPL LICENSE SUMMARY | | 33 | * GPL LICENSE SUMMARY |
34 | * | | 34 | * |
35 | * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. | | 35 | * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. |
36 | * | | 36 | * |
37 | * This program is free software; you can redistribute it and/or modify | | 37 | * This program is free software; you can redistribute it and/or modify |
38 | * it under the terms of version 2 of the GNU General Public License as | | 38 | * it under the terms of version 2 of the GNU General Public License as |
39 | * published by the Free Software Foundation. | | 39 | * published by the Free Software Foundation. |
40 | * | | 40 | * |
41 | * This program is distributed in the hope that it will be useful, but | | 41 | * This program is distributed in the hope that it will be useful, but |
42 | * WITHOUT ANY WARRANTY; without even the implied warranty of | | 42 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
43 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | | 43 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
44 | * General Public License for more details. | | 44 | * General Public License for more details. |
45 | * | | 45 | * |
46 | * You should have received a copy of the GNU General Public License | | 46 | * You should have received a copy of the GNU General Public License |
47 | * along with this program; if not, write to the Free Software | | 47 | * along with this program; if not, write to the Free Software |
48 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | | 48 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, |
49 | * USA | | 49 | * USA |
50 | * | | 50 | * |
51 | * The full GNU General Public License is included in this distribution | | 51 | * The full GNU General Public License is included in this distribution |
52 | * in the file called COPYING. | | 52 | * in the file called COPYING. |
53 | * | | 53 | * |
54 | * Contact Information: | | 54 | * Contact Information: |
55 | * Intel Linux Wireless <ilw@linux.intel.com> | | 55 | * Intel Linux Wireless <ilw@linux.intel.com> |
56 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | | 56 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
57 | * | | 57 | * |
58 | * | | 58 | * |
59 | * BSD LICENSE | | 59 | * BSD LICENSE |
60 | * | | 60 | * |
61 | * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. | | 61 | * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. |
62 | * All rights reserved. | | 62 | * All rights reserved. |
63 | * | | 63 | * |
64 | * Redistribution and use in source and binary forms, with or without | | 64 | * Redistribution and use in source and binary forms, with or without |
65 | * modification, are permitted provided that the following conditions | | 65 | * modification, are permitted provided that the following conditions |
66 | * are met: | | 66 | * are met: |
67 | * | | 67 | * |
68 | * * Redistributions of source code must retain the above copyright | | 68 | * * Redistributions of source code must retain the above copyright |
69 | * notice, this list of conditions and the following disclaimer. | | 69 | * notice, this list of conditions and the following disclaimer. |
70 | * * Redistributions in binary form must reproduce the above copyright | | 70 | * * Redistributions in binary form must reproduce the above copyright |
71 | * notice, this list of conditions and the following disclaimer in | | 71 | * notice, this list of conditions and the following disclaimer in |
72 | * the documentation and/or other materials provided with the | | 72 | * the documentation and/or other materials provided with the |
73 | * distribution. | | 73 | * distribution. |
74 | * * Neither the name Intel Corporation nor the names of its | | 74 | * * Neither the name Intel Corporation nor the names of its |
75 | * contributors may be used to endorse or promote products derived | | 75 | * contributors may be used to endorse or promote products derived |
76 | * from this software without specific prior written permission. | | 76 | * from this software without specific prior written permission. |
77 | * | | 77 | * |
78 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | | 78 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
79 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | | 79 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
80 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | | 80 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
81 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | | 81 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
82 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | | 82 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
83 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | | 83 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
84 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | 84 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
85 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | 85 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
86 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | 86 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
87 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | | 87 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
88 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | 88 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
89 | */ | | 89 | */ |
90 | | | 90 | |
91 | /*- | | 91 | /*- |
92 | * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> | | 92 | * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> |
93 | * | | 93 | * |
94 | * Permission to use, copy, modify, and distribute this software for any | | 94 | * Permission to use, copy, modify, and distribute this software for any |
95 | * purpose with or without fee is hereby granted, provided that the above | | 95 | * purpose with or without fee is hereby granted, provided that the above |
96 | * copyright notice and this permission notice appear in all copies. | | 96 | * copyright notice and this permission notice appear in all copies. |
97 | * | | 97 | * |
98 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 98 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
99 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 99 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
100 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 100 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
101 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 101 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
102 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 102 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
103 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 103 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
104 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 104 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
105 | */ | | 105 | */ |
106 | | | 106 | |
107 | #include <sys/cdefs.h> | | 107 | #include <sys/cdefs.h> |
108 | __KERNEL_RCSID(0, "$NetBSD: if_iwm.c,v 1.20 2015/03/03 09:27:35 nonaka Exp $"); | | 108 | __KERNEL_RCSID(0, "$NetBSD: if_iwm.c,v 1.21 2015/03/03 09:34:40 nonaka Exp $"); |
109 | | | 109 | |
110 | #include <sys/param.h> | | 110 | #include <sys/param.h> |
111 | #include <sys/conf.h> | | 111 | #include <sys/conf.h> |
112 | #include <sys/kernel.h> | | 112 | #include <sys/kernel.h> |
113 | #include <sys/kmem.h> | | 113 | #include <sys/kmem.h> |
114 | #include <sys/mbuf.h> | | 114 | #include <sys/mbuf.h> |
115 | #include <sys/mutex.h> | | 115 | #include <sys/mutex.h> |
116 | #include <sys/proc.h> | | 116 | #include <sys/proc.h> |
117 | #include <sys/socket.h> | | 117 | #include <sys/socket.h> |
118 | #include <sys/sockio.h> | | 118 | #include <sys/sockio.h> |
119 | #include <sys/systm.h> | | 119 | #include <sys/systm.h> |
120 | | | 120 | |
121 | #include <sys/cpu.h> | | 121 | #include <sys/cpu.h> |
122 | #include <sys/bus.h> | | 122 | #include <sys/bus.h> |
123 | #include <sys/workqueue.h> | | 123 | #include <sys/workqueue.h> |
124 | #include <machine/endian.h> | | 124 | #include <machine/endian.h> |
125 | #include <machine/intr.h> | | 125 | #include <machine/intr.h> |
126 | | | 126 | |
127 | #include <dev/pci/pcireg.h> | | 127 | #include <dev/pci/pcireg.h> |
128 | #include <dev/pci/pcivar.h> | | 128 | #include <dev/pci/pcivar.h> |
129 | #include <dev/pci/pcidevs.h> | | 129 | #include <dev/pci/pcidevs.h> |
130 | #include <dev/firmload.h> | | 130 | #include <dev/firmload.h> |
131 | | | 131 | |
132 | #include <net/bpf.h> | | 132 | #include <net/bpf.h> |
133 | #include <net/if.h> | | 133 | #include <net/if.h> |
134 | #include <net/if_arp.h> | | 134 | #include <net/if_arp.h> |
135 | #include <net/if_dl.h> | | 135 | #include <net/if_dl.h> |
136 | #include <net/if_media.h> | | 136 | #include <net/if_media.h> |
137 | #include <net/if_types.h> | | 137 | #include <net/if_types.h> |
138 | #include <net/if_ether.h> | | 138 | #include <net/if_ether.h> |
139 | | | 139 | |
140 | #include <netinet/in.h> | | 140 | #include <netinet/in.h> |
141 | #include <netinet/in_systm.h> | | 141 | #include <netinet/in_systm.h> |
142 | #include <netinet/ip.h> | | 142 | #include <netinet/ip.h> |
143 | | | 143 | |
144 | #include <net80211/ieee80211_var.h> | | 144 | #include <net80211/ieee80211_var.h> |
145 | #include <net80211/ieee80211_amrr.h> | | 145 | #include <net80211/ieee80211_amrr.h> |
146 | #include <net80211/ieee80211_radiotap.h> | | 146 | #include <net80211/ieee80211_radiotap.h> |
147 | | | 147 | |
148 | #define DEVNAME(_s) device_xname((_s)->sc_dev) | | 148 | #define DEVNAME(_s) device_xname((_s)->sc_dev) |
149 | #define IC2IFP(_ic_) ((_ic_)->ic_ifp) | | 149 | #define IC2IFP(_ic_) ((_ic_)->ic_ifp) |
150 | | | 150 | |
151 | #define le16_to_cpup(_a_) (le16toh(*(const uint16_t *)(_a_))) | | 151 | #define le16_to_cpup(_a_) (le16toh(*(const uint16_t *)(_a_))) |
152 | #define le32_to_cpup(_a_) (le32toh(*(const uint32_t *)(_a_))) | | 152 | #define le32_to_cpup(_a_) (le32toh(*(const uint32_t *)(_a_))) |
153 | | | 153 | |
154 | #ifdef IWM_DEBUG | | 154 | #ifdef IWM_DEBUG |
155 | #define DPRINTF(x) do { if (iwm_debug > 0) printf x; } while (0) | | 155 | #define DPRINTF(x) do { if (iwm_debug > 0) printf x; } while (0) |
156 | #define DPRINTFN(n, x) do { if (iwm_debug >= (n)) printf x; } while (0) | | 156 | #define DPRINTFN(n, x) do { if (iwm_debug >= (n)) printf x; } while (0) |
157 | int iwm_debug = 1; | | 157 | int iwm_debug = 1; |
158 | #else | | 158 | #else |
159 | #define DPRINTF(x) do { ; } while (0) | | 159 | #define DPRINTF(x) do { ; } while (0) |
160 | #define DPRINTFN(n, x) do { ; } while (0) | | 160 | #define DPRINTFN(n, x) do { ; } while (0) |
161 | #endif | | 161 | #endif |
162 | | | 162 | |
163 | #include <dev/pci/if_iwmreg.h> | | 163 | #include <dev/pci/if_iwmreg.h> |
164 | #include <dev/pci/if_iwmvar.h> | | 164 | #include <dev/pci/if_iwmvar.h> |
165 | | | 165 | |
166 | static const uint8_t iwm_nvm_channels[] = { | | 166 | static const uint8_t iwm_nvm_channels[] = { |
167 | /* 2.4 GHz */ | | 167 | /* 2.4 GHz */ |
168 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | | 168 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
169 | /* 5 GHz */ | | 169 | /* 5 GHz */ |
170 | 36, 40, 44 , 48, 52, 56, 60, 64, | | 170 | 36, 40, 44 , 48, 52, 56, 60, 64, |
171 | 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, | | 171 | 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, |
172 | 149, 153, 157, 161, 165 | | 172 | 149, 153, 157, 161, 165 |
173 | }; | | 173 | }; |
174 | #define IWM_NUM_2GHZ_CHANNELS 14 | | 174 | #define IWM_NUM_2GHZ_CHANNELS 14 |
175 | | | 175 | |
176 | /* It looks like 11a TX is broken, unfortunately. */ | | 176 | /* It looks like 11a TX is broken, unfortunately. */ |
177 | #define IWM_NO_5GHZ 1 | | 177 | #define IWM_NO_5GHZ 1 |
178 | | | 178 | |
179 | static const struct iwm_rate { | | 179 | static const struct iwm_rate { |
180 | uint8_t rate; | | 180 | uint8_t rate; |
181 | uint8_t plcp; | | 181 | uint8_t plcp; |
182 | } iwm_rates[] = { | | 182 | } iwm_rates[] = { |
183 | { 2, IWM_RATE_1M_PLCP }, | | 183 | { 2, IWM_RATE_1M_PLCP }, |
184 | { 4, IWM_RATE_2M_PLCP }, | | 184 | { 4, IWM_RATE_2M_PLCP }, |
185 | { 11, IWM_RATE_5M_PLCP }, | | 185 | { 11, IWM_RATE_5M_PLCP }, |
186 | { 22, IWM_RATE_11M_PLCP }, | | 186 | { 22, IWM_RATE_11M_PLCP }, |
187 | { 12, IWM_RATE_6M_PLCP }, | | 187 | { 12, IWM_RATE_6M_PLCP }, |
188 | { 18, IWM_RATE_9M_PLCP }, | | 188 | { 18, IWM_RATE_9M_PLCP }, |
189 | { 24, IWM_RATE_12M_PLCP }, | | 189 | { 24, IWM_RATE_12M_PLCP }, |
190 | { 36, IWM_RATE_18M_PLCP }, | | 190 | { 36, IWM_RATE_18M_PLCP }, |
191 | { 48, IWM_RATE_24M_PLCP }, | | 191 | { 48, IWM_RATE_24M_PLCP }, |
192 | { 72, IWM_RATE_36M_PLCP }, | | 192 | { 72, IWM_RATE_36M_PLCP }, |
193 | { 96, IWM_RATE_48M_PLCP }, | | 193 | { 96, IWM_RATE_48M_PLCP }, |
194 | { 108, IWM_RATE_54M_PLCP }, | | 194 | { 108, IWM_RATE_54M_PLCP }, |
195 | }; | | 195 | }; |
196 | #define IWM_RIDX_CCK 0 | | 196 | #define IWM_RIDX_CCK 0 |
197 | #define IWM_RIDX_OFDM 4 | | 197 | #define IWM_RIDX_OFDM 4 |
198 | #define IWM_RIDX_MAX (__arraycount(iwm_rates)-1) | | 198 | #define IWM_RIDX_MAX (__arraycount(iwm_rates)-1) |
199 | #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) | | 199 | #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) |
200 | #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) | | 200 | #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) |
201 | | | 201 | |
202 | struct iwm_newstate_state { | | 202 | struct iwm_newstate_state { |
203 | struct work ns_wk; | | 203 | struct work ns_wk; |
204 | enum ieee80211_state ns_nstate; | | 204 | enum ieee80211_state ns_nstate; |
205 | int ns_arg; | | 205 | int ns_arg; |
206 | int ns_generation; | | 206 | int ns_generation; |
207 | }; | | 207 | }; |
208 | | | 208 | |
209 | static int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t); | | 209 | static int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t); |
210 | static int iwm_firmware_store_section(struct iwm_softc *, | | 210 | static int iwm_firmware_store_section(struct iwm_softc *, |
211 | enum iwm_ucode_type, uint8_t *, size_t); | | 211 | enum iwm_ucode_type, uint8_t *, size_t); |
212 | static int iwm_set_default_calib(struct iwm_softc *, const void *); | | 212 | static int iwm_set_default_calib(struct iwm_softc *, const void *); |
213 | static int iwm_read_firmware(struct iwm_softc *); | | 213 | static int iwm_read_firmware(struct iwm_softc *); |
214 | static uint32_t iwm_read_prph(struct iwm_softc *, uint32_t); | | 214 | static uint32_t iwm_read_prph(struct iwm_softc *, uint32_t); |
215 | static void iwm_write_prph(struct iwm_softc *, uint32_t, uint32_t); | | 215 | static void iwm_write_prph(struct iwm_softc *, uint32_t, uint32_t); |
216 | #ifdef IWM_DEBUG | | 216 | #ifdef IWM_DEBUG |
217 | static int iwm_read_mem(struct iwm_softc *, uint32_t, void *, int); | | 217 | static int iwm_read_mem(struct iwm_softc *, uint32_t, void *, int); |
218 | #endif | | 218 | #endif |
219 | static int iwm_write_mem(struct iwm_softc *, uint32_t, const void *, int); | | 219 | static int iwm_write_mem(struct iwm_softc *, uint32_t, const void *, int); |
220 | static int iwm_write_mem32(struct iwm_softc *, uint32_t, uint32_t); | | 220 | static int iwm_write_mem32(struct iwm_softc *, uint32_t, uint32_t); |
221 | static int iwm_poll_bit(struct iwm_softc *, int, uint32_t, uint32_t, int); | | 221 | static int iwm_poll_bit(struct iwm_softc *, int, uint32_t, uint32_t, int); |
222 | static int iwm_nic_lock(struct iwm_softc *); | | 222 | static int iwm_nic_lock(struct iwm_softc *); |
223 | static void iwm_nic_unlock(struct iwm_softc *); | | 223 | static void iwm_nic_unlock(struct iwm_softc *); |
224 | static void iwm_set_bits_mask_prph(struct iwm_softc *, uint32_t, uint32_t, | | 224 | static void iwm_set_bits_mask_prph(struct iwm_softc *, uint32_t, uint32_t, |
225 | uint32_t); | | 225 | uint32_t); |
226 | static void iwm_set_bits_prph(struct iwm_softc *, uint32_t, uint32_t); | | 226 | static void iwm_set_bits_prph(struct iwm_softc *, uint32_t, uint32_t); |
227 | static void iwm_clear_bits_prph(struct iwm_softc *, uint32_t, uint32_t); | | 227 | static void iwm_clear_bits_prph(struct iwm_softc *, uint32_t, uint32_t); |
228 | static int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, | | 228 | static int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, |
229 | bus_size_t, bus_size_t); | | 229 | bus_size_t, bus_size_t); |
230 | static void iwm_dma_contig_free(struct iwm_dma_info *); | | 230 | static void iwm_dma_contig_free(struct iwm_dma_info *); |
231 | static int iwm_alloc_fwmem(struct iwm_softc *); | | 231 | static int iwm_alloc_fwmem(struct iwm_softc *); |
232 | static void iwm_free_fwmem(struct iwm_softc *); | | 232 | static void iwm_free_fwmem(struct iwm_softc *); |
233 | static int iwm_alloc_sched(struct iwm_softc *); | | 233 | static int iwm_alloc_sched(struct iwm_softc *); |
234 | static void iwm_free_sched(struct iwm_softc *); | | 234 | static void iwm_free_sched(struct iwm_softc *); |
235 | static int iwm_alloc_kw(struct iwm_softc *); | | 235 | static int iwm_alloc_kw(struct iwm_softc *); |
236 | static void iwm_free_kw(struct iwm_softc *); | | 236 | static void iwm_free_kw(struct iwm_softc *); |
237 | static int iwm_alloc_ict(struct iwm_softc *); | | 237 | static int iwm_alloc_ict(struct iwm_softc *); |
238 | static void iwm_free_ict(struct iwm_softc *); | | 238 | static void iwm_free_ict(struct iwm_softc *); |
239 | static int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); | | 239 | static int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); |
240 | static void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); | | 240 | static void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); |
241 | static void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); | | 241 | static void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); |
242 | static int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, | | 242 | static int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, |
243 | int); | | 243 | int); |
244 | static void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); | | 244 | static void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); |
245 | static void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); | | 245 | static void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); |
246 | static void iwm_enable_rfkill_int(struct iwm_softc *); | | 246 | static void iwm_enable_rfkill_int(struct iwm_softc *); |
247 | static int iwm_check_rfkill(struct iwm_softc *); | | 247 | static int iwm_check_rfkill(struct iwm_softc *); |
248 | static void iwm_enable_interrupts(struct iwm_softc *); | | 248 | static void iwm_enable_interrupts(struct iwm_softc *); |
249 | static void iwm_restore_interrupts(struct iwm_softc *); | | 249 | static void iwm_restore_interrupts(struct iwm_softc *); |
250 | static void iwm_disable_interrupts(struct iwm_softc *); | | 250 | static void iwm_disable_interrupts(struct iwm_softc *); |
251 | static void iwm_ict_reset(struct iwm_softc *); | | 251 | static void iwm_ict_reset(struct iwm_softc *); |
252 | static int iwm_set_hw_ready(struct iwm_softc *); | | 252 | static int iwm_set_hw_ready(struct iwm_softc *); |
253 | static int iwm_prepare_card_hw(struct iwm_softc *); | | 253 | static int iwm_prepare_card_hw(struct iwm_softc *); |
254 | static void iwm_apm_config(struct iwm_softc *); | | 254 | static void iwm_apm_config(struct iwm_softc *); |
255 | static int iwm_apm_init(struct iwm_softc *); | | 255 | static int iwm_apm_init(struct iwm_softc *); |
256 | static void iwm_apm_stop(struct iwm_softc *); | | 256 | static void iwm_apm_stop(struct iwm_softc *); |
257 | static int iwm_allow_mcast(struct iwm_softc *); | | 257 | static int iwm_allow_mcast(struct iwm_softc *); |
258 | static int iwm_start_hw(struct iwm_softc *); | | 258 | static int iwm_start_hw(struct iwm_softc *); |
259 | static void iwm_stop_device(struct iwm_softc *); | | 259 | static void iwm_stop_device(struct iwm_softc *); |
260 | static void iwm_set_pwr(struct iwm_softc *); | | 260 | static void iwm_set_pwr(struct iwm_softc *); |
261 | static void iwm_mvm_nic_config(struct iwm_softc *); | | 261 | static void iwm_mvm_nic_config(struct iwm_softc *); |
262 | static int iwm_nic_rx_init(struct iwm_softc *); | | 262 | static int iwm_nic_rx_init(struct iwm_softc *); |
263 | static int iwm_nic_tx_init(struct iwm_softc *); | | 263 | static int iwm_nic_tx_init(struct iwm_softc *); |
264 | static int iwm_nic_init(struct iwm_softc *); | | 264 | static int iwm_nic_init(struct iwm_softc *); |
265 | static void iwm_enable_txq(struct iwm_softc *, int, int); | | 265 | static void iwm_enable_txq(struct iwm_softc *, int, int); |
266 | static int iwm_post_alive(struct iwm_softc *); | | 266 | static int iwm_post_alive(struct iwm_softc *); |
267 | static int iwm_is_valid_channel(uint16_t); | | 267 | static int iwm_is_valid_channel(uint16_t); |
268 | static uint8_t iwm_ch_id_to_ch_index(uint16_t); | | 268 | static uint8_t iwm_ch_id_to_ch_index(uint16_t); |
269 | static uint16_t iwm_channel_id_to_papd(uint16_t); | | 269 | static uint16_t iwm_channel_id_to_papd(uint16_t); |
270 | static uint16_t iwm_channel_id_to_txp(struct iwm_softc *, uint16_t); | | 270 | static uint16_t iwm_channel_id_to_txp(struct iwm_softc *, uint16_t); |
271 | static int iwm_phy_db_get_section_data(struct iwm_softc *, uint32_t, | | 271 | static int iwm_phy_db_get_section_data(struct iwm_softc *, uint32_t, |
272 | uint8_t **, uint16_t *, uint16_t); | | 272 | uint8_t **, uint16_t *, uint16_t); |
273 | static int iwm_send_phy_db_cmd(struct iwm_softc *, uint16_t, uint16_t, | | 273 | static int iwm_send_phy_db_cmd(struct iwm_softc *, uint16_t, uint16_t, |
274 | void *); | | 274 | void *); |
275 | static int iwm_send_phy_db_data(struct iwm_softc *); | | 275 | static int iwm_send_phy_db_data(struct iwm_softc *); |
276 | static int iwm_send_phy_db_data(struct iwm_softc *); | | 276 | static int iwm_send_phy_db_data(struct iwm_softc *); |
277 | static void iwm_mvm_te_v2_to_v1(const struct iwm_time_event_cmd_v2 *, | | 277 | static void iwm_mvm_te_v2_to_v1(const struct iwm_time_event_cmd_v2 *, |
278 | struct iwm_time_event_cmd_v1 *); | | 278 | struct iwm_time_event_cmd_v1 *); |
279 | static int iwm_mvm_send_time_event_cmd(struct iwm_softc *, | | 279 | static int iwm_mvm_send_time_event_cmd(struct iwm_softc *, |
280 | const struct iwm_time_event_cmd_v2 *); | | 280 | const struct iwm_time_event_cmd_v2 *); |
281 | static int iwm_mvm_time_event_send_add(struct iwm_softc *, | | 281 | static int iwm_mvm_time_event_send_add(struct iwm_softc *, |
282 | struct iwm_node *, void *, struct iwm_time_event_cmd_v2 *); | | 282 | struct iwm_node *, void *, struct iwm_time_event_cmd_v2 *); |
283 | static void iwm_mvm_protect_session(struct iwm_softc *, struct iwm_node *, | | 283 | static void iwm_mvm_protect_session(struct iwm_softc *, struct iwm_node *, |
284 | uint32_t, uint32_t, uint32_t); | | 284 | uint32_t, uint32_t, uint32_t); |
285 | static int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, | | 285 | static int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, |
286 | uint16_t, uint8_t *, uint16_t *); | | 286 | uint16_t, uint8_t *, uint16_t *); |
287 | static int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, | | 287 | static int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, |
288 | uint16_t *); | | 288 | uint16_t *); |
289 | static void iwm_init_channel_map(struct iwm_softc *, | | 289 | static void iwm_init_channel_map(struct iwm_softc *, |
290 | const uint16_t * const); | | 290 | const uint16_t * const); |
291 | static int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, | | 291 | static int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, |
292 | const uint16_t *, const uint16_t *, uint8_t, uint8_t); | | 292 | const uint16_t *, const uint16_t *, uint8_t, uint8_t); |
293 | static int iwm_nvm_init(struct iwm_softc *); | | 293 | static int iwm_nvm_init(struct iwm_softc *); |
294 | static int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, | | 294 | static int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, |
295 | const uint8_t *, uint32_t); | | 295 | const uint8_t *, uint32_t); |
296 | static int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); | | 296 | static int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); |
297 | static int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); | | 297 | static int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); |
298 | static int iwm_fw_alive(struct iwm_softc *, uint32_t); | | 298 | static int iwm_fw_alive(struct iwm_softc *, uint32_t); |
299 | static int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); | | 299 | static int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); |
300 | static int iwm_send_phy_cfg_cmd(struct iwm_softc *); | | 300 | static int iwm_send_phy_cfg_cmd(struct iwm_softc *); |
301 | static int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, | | 301 | static int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, |
302 | enum iwm_ucode_type); | | 302 | enum iwm_ucode_type); |
303 | static int iwm_run_init_mvm_ucode(struct iwm_softc *, int); | | 303 | static int iwm_run_init_mvm_ucode(struct iwm_softc *, int); |
304 | static int iwm_rx_addbuf(struct iwm_softc *, int, int); | | 304 | static int iwm_rx_addbuf(struct iwm_softc *, int, int); |
305 | static int iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); | | 305 | static int iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); |
306 | static int iwm_mvm_get_signal_strength(struct iwm_softc *, | | 306 | static int iwm_mvm_get_signal_strength(struct iwm_softc *, |
307 | struct iwm_rx_phy_info *); | | 307 | struct iwm_rx_phy_info *); |
308 | static void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *, | | 308 | static void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *, |
309 | struct iwm_rx_packet *, struct iwm_rx_data *); | | 309 | struct iwm_rx_packet *, struct iwm_rx_data *); |
310 | static int iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *); | | 310 | static int iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *); |
311 | static void iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, | | 311 | static void iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, |
312 | struct iwm_rx_data *); | | 312 | struct iwm_rx_data *); |
313 | static void iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, | | 313 | static void iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, |
314 | struct iwm_rx_packet *, struct iwm_node *); | | 314 | struct iwm_rx_packet *, struct iwm_node *); |
315 | static void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, | | 315 | static void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, |
316 | struct iwm_rx_data *); | | 316 | struct iwm_rx_data *); |
317 | static int iwm_mvm_binding_cmd(struct iwm_softc *, struct iwm_node *, | | 317 | static int iwm_mvm_binding_cmd(struct iwm_softc *, struct iwm_node *, |
318 | uint32_t); | | 318 | uint32_t); |
319 | static int iwm_mvm_binding_update(struct iwm_softc *, struct iwm_node *, | | 319 | static int iwm_mvm_binding_update(struct iwm_softc *, struct iwm_node *, |
320 | int); | | 320 | int); |
321 | static int iwm_mvm_binding_add_vif(struct iwm_softc *, struct iwm_node *); | | 321 | static int iwm_mvm_binding_add_vif(struct iwm_softc *, struct iwm_node *); |
322 | static void iwm_mvm_phy_ctxt_cmd_hdr(struct iwm_softc *, | | 322 | static void iwm_mvm_phy_ctxt_cmd_hdr(struct iwm_softc *, |
323 | struct iwm_mvm_phy_ctxt *, struct iwm_phy_context_cmd *, | | 323 | struct iwm_mvm_phy_ctxt *, struct iwm_phy_context_cmd *, |
324 | uint32_t, uint32_t); | | 324 | uint32_t, uint32_t); |
325 | static void iwm_mvm_phy_ctxt_cmd_data(struct iwm_softc *, | | 325 | static void iwm_mvm_phy_ctxt_cmd_data(struct iwm_softc *, |
326 | struct iwm_phy_context_cmd *, struct ieee80211_channel *, | | 326 | struct iwm_phy_context_cmd *, struct ieee80211_channel *, |
327 | uint8_t, uint8_t); | | 327 | uint8_t, uint8_t); |
328 | static int iwm_mvm_phy_ctxt_apply(struct iwm_softc *, | | 328 | static int iwm_mvm_phy_ctxt_apply(struct iwm_softc *, |
329 | struct iwm_mvm_phy_ctxt *, uint8_t, uint8_t, uint32_t, | | 329 | struct iwm_mvm_phy_ctxt *, uint8_t, uint8_t, uint32_t, |
330 | uint32_t); | | 330 | uint32_t); |
331 | static int iwm_mvm_phy_ctxt_add(struct iwm_softc *, | | 331 | static int iwm_mvm_phy_ctxt_add(struct iwm_softc *, |
332 | struct iwm_mvm_phy_ctxt *, struct ieee80211_channel *, | | 332 | struct iwm_mvm_phy_ctxt *, struct ieee80211_channel *, |
333 | uint8_t, uint8_t); | | 333 | uint8_t, uint8_t); |
334 | static int iwm_mvm_phy_ctxt_changed(struct iwm_softc *, | | 334 | static int iwm_mvm_phy_ctxt_changed(struct iwm_softc *, |
335 | struct iwm_mvm_phy_ctxt *, struct ieee80211_channel *, | | 335 | struct iwm_mvm_phy_ctxt *, struct ieee80211_channel *, |
336 | uint8_t, uint8_t); | | 336 | uint8_t, uint8_t); |
337 | static int iwm_send_cmd(struct iwm_softc *, struct iwm_host_cmd *); | | 337 | static int iwm_send_cmd(struct iwm_softc *, struct iwm_host_cmd *); |
338 | static int iwm_mvm_send_cmd_pdu(struct iwm_softc *, uint8_t, uint32_t, | | 338 | static int iwm_mvm_send_cmd_pdu(struct iwm_softc *, uint8_t, uint32_t, |
339 | uint16_t, const void *); | | 339 | uint16_t, const void *); |
340 | static int iwm_mvm_send_cmd_status(struct iwm_softc *, | | 340 | static int iwm_mvm_send_cmd_status(struct iwm_softc *, |
341 | struct iwm_host_cmd *, uint32_t *); | | 341 | struct iwm_host_cmd *, uint32_t *); |
342 | static int iwm_mvm_send_cmd_pdu_status(struct iwm_softc *, uint8_t, | | 342 | static int iwm_mvm_send_cmd_pdu_status(struct iwm_softc *, uint8_t, |
343 | uint16_t, const void *, uint32_t *); | | 343 | uint16_t, const void *, uint32_t *); |
344 | static void iwm_free_resp(struct iwm_softc *, struct iwm_host_cmd *); | | 344 | static void iwm_free_resp(struct iwm_softc *, struct iwm_host_cmd *); |
345 | static void iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *); | | 345 | static void iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *); |
346 | #if 0 | | 346 | #if 0 |
347 | static void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, | | 347 | static void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, |
348 | uint16_t); | | 348 | uint16_t); |
349 | #endif | | 349 | #endif |
350 | static const struct iwm_rate *iwm_tx_fill_cmd(struct iwm_softc *, | | 350 | static const struct iwm_rate *iwm_tx_fill_cmd(struct iwm_softc *, |
351 | struct iwm_node *, struct ieee80211_frame *, | | 351 | struct iwm_node *, struct ieee80211_frame *, |
352 | struct iwm_tx_cmd *); | | 352 | struct iwm_tx_cmd *); |
353 | static int iwm_tx(struct iwm_softc *, struct mbuf *, | | 353 | static int iwm_tx(struct iwm_softc *, struct mbuf *, |
354 | struct ieee80211_node *, int); | | 354 | struct ieee80211_node *, int); |
355 | static int iwm_mvm_beacon_filter_send_cmd(struct iwm_softc *, | | 355 | static int iwm_mvm_beacon_filter_send_cmd(struct iwm_softc *, |
356 | struct iwm_beacon_filter_cmd *); | | 356 | struct iwm_beacon_filter_cmd *); |
357 | static void iwm_mvm_beacon_filter_set_cqm_params(struct iwm_softc *, | | 357 | static void iwm_mvm_beacon_filter_set_cqm_params(struct iwm_softc *, |
358 | struct iwm_node *, struct iwm_beacon_filter_cmd *); | | 358 | struct iwm_node *, struct iwm_beacon_filter_cmd *); |
359 | static int iwm_mvm_update_beacon_abort(struct iwm_softc *, | | 359 | static int iwm_mvm_update_beacon_abort(struct iwm_softc *, |
360 | struct iwm_node *, int); | | 360 | struct iwm_node *, int); |
361 | static void iwm_mvm_power_log(struct iwm_softc *, | | 361 | static void iwm_mvm_power_log(struct iwm_softc *, |
362 | struct iwm_mac_power_cmd *); | | 362 | struct iwm_mac_power_cmd *); |
363 | static void iwm_mvm_power_build_cmd(struct iwm_softc *, struct iwm_node *, | | 363 | static void iwm_mvm_power_build_cmd(struct iwm_softc *, struct iwm_node *, |
364 | struct iwm_mac_power_cmd *); | | 364 | struct iwm_mac_power_cmd *); |
365 | static int iwm_mvm_power_mac_update_mode(struct iwm_softc *, | | 365 | static int iwm_mvm_power_mac_update_mode(struct iwm_softc *, |
366 | struct iwm_node *); | | 366 | struct iwm_node *); |
367 | static int iwm_mvm_power_update_device(struct iwm_softc *); | | 367 | static int iwm_mvm_power_update_device(struct iwm_softc *); |
368 | static int iwm_mvm_enable_beacon_filter(struct iwm_softc *, | | 368 | static int iwm_mvm_enable_beacon_filter(struct iwm_softc *, |
369 | struct iwm_node *); | | 369 | struct iwm_node *); |
370 | static int iwm_mvm_disable_beacon_filter(struct iwm_softc *, | | 370 | static int iwm_mvm_disable_beacon_filter(struct iwm_softc *, |
371 | struct iwm_node *); | | 371 | struct iwm_node *); |
372 | static void iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *, | | 372 | static void iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *, |
373 | struct iwm_mvm_add_sta_cmd_v5 *); | | 373 | struct iwm_mvm_add_sta_cmd_v5 *); |
374 | static int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, | | 374 | static int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, |
375 | struct iwm_mvm_add_sta_cmd_v6 *, int *); | | 375 | struct iwm_mvm_add_sta_cmd_v6 *, int *); |
376 | static int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *, | | 376 | static int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *, |
377 | int); | | 377 | int); |
378 | static int iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *); | | 378 | static int iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *); |
379 | static int iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *); | | 379 | static int iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *); |
380 | static int iwm_mvm_add_int_sta_common(struct iwm_softc *, | | 380 | static int iwm_mvm_add_int_sta_common(struct iwm_softc *, |
381 | struct iwm_int_sta *, const uint8_t *, uint16_t, uint16_t); | | 381 | struct iwm_int_sta *, const uint8_t *, uint16_t, uint16_t); |
382 | static int iwm_mvm_add_aux_sta(struct iwm_softc *); | | 382 | static int iwm_mvm_add_aux_sta(struct iwm_softc *); |
383 | static uint16_t iwm_mvm_scan_rx_chain(struct iwm_softc *); | | 383 | static uint16_t iwm_mvm_scan_rx_chain(struct iwm_softc *); |
384 | static uint32_t iwm_mvm_scan_max_out_time(struct iwm_softc *, uint32_t, int); | | 384 | static uint32_t iwm_mvm_scan_max_out_time(struct iwm_softc *, uint32_t, int); |
385 | static uint32_t iwm_mvm_scan_suspend_time(struct iwm_softc *, int); | | 385 | static uint32_t iwm_mvm_scan_suspend_time(struct iwm_softc *, int); |
386 | static uint32_t iwm_mvm_scan_rxon_flags(struct iwm_softc *, int); | | 386 | static uint32_t iwm_mvm_scan_rxon_flags(struct iwm_softc *, int); |
387 | static uint32_t iwm_mvm_scan_rate_n_flags(struct iwm_softc *, int, int); | | 387 | static uint32_t iwm_mvm_scan_rate_n_flags(struct iwm_softc *, int, int); |
388 | static uint16_t iwm_mvm_get_active_dwell(struct iwm_softc *, int, int); | | 388 | static uint16_t iwm_mvm_get_active_dwell(struct iwm_softc *, int, int); |
389 | static uint16_t iwm_mvm_get_passive_dwell(struct iwm_softc *, int); | | 389 | static uint16_t iwm_mvm_get_passive_dwell(struct iwm_softc *, int); |
390 | static int iwm_mvm_scan_fill_channels(struct iwm_softc *, | | 390 | static int iwm_mvm_scan_fill_channels(struct iwm_softc *, |
391 | struct iwm_scan_cmd *, int, int, int); | | 391 | struct iwm_scan_cmd *, int, int, int); |
392 | static uint16_t iwm_mvm_fill_probe_req(struct iwm_softc *, | | 392 | static uint16_t iwm_mvm_fill_probe_req(struct iwm_softc *, |
393 | struct ieee80211_frame *, const uint8_t *, int, | | 393 | struct ieee80211_frame *, const uint8_t *, int, |
394 | const uint8_t *, int, const uint8_t *, int, int); | | 394 | const uint8_t *, int, const uint8_t *, int, int); |
395 | static int iwm_mvm_scan_request(struct iwm_softc *, int, int, uint8_t *, | | 395 | static int iwm_mvm_scan_request(struct iwm_softc *, int, int, uint8_t *, |
396 | int); | | 396 | int); |
397 | static void iwm_mvm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, | | 397 | static void iwm_mvm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, |
398 | int *); | | 398 | int *); |
399 | static void iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *, | | 399 | static void iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *, |
400 | struct iwm_node *, struct iwm_mac_ctx_cmd *, uint32_t); | | 400 | struct iwm_node *, struct iwm_mac_ctx_cmd *, uint32_t); |
401 | static int iwm_mvm_mac_ctxt_send_cmd(struct iwm_softc *, | | 401 | static int iwm_mvm_mac_ctxt_send_cmd(struct iwm_softc *, |
402 | struct iwm_mac_ctx_cmd *); | | 402 | struct iwm_mac_ctx_cmd *); |
403 | static void iwm_mvm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, | | 403 | static void iwm_mvm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, |
404 | struct iwm_node *, struct iwm_mac_data_sta *, int); | | 404 | struct iwm_node *, struct iwm_mac_data_sta *, int); |
405 | static int iwm_mvm_mac_ctxt_cmd_station(struct iwm_softc *, | | 405 | static int iwm_mvm_mac_ctxt_cmd_station(struct iwm_softc *, |
406 | struct iwm_node *, uint32_t); | | 406 | struct iwm_node *, uint32_t); |
407 | static int iwm_mvm_mac_ctx_send(struct iwm_softc *, struct iwm_node *, | | 407 | static int iwm_mvm_mac_ctx_send(struct iwm_softc *, struct iwm_node *, |
408 | uint32_t); | | 408 | uint32_t); |
409 | static int iwm_mvm_mac_ctxt_add(struct iwm_softc *, struct iwm_node *); | | 409 | static int iwm_mvm_mac_ctxt_add(struct iwm_softc *, struct iwm_node *); |
410 | static int iwm_mvm_mac_ctxt_changed(struct iwm_softc *, struct iwm_node *); | | 410 | static int iwm_mvm_mac_ctxt_changed(struct iwm_softc *, struct iwm_node *); |
411 | static int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *); | | 411 | static int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *); |
412 | static int iwm_auth(struct iwm_softc *); | | 412 | static int iwm_auth(struct iwm_softc *); |
413 | static int iwm_assoc(struct iwm_softc *); | | 413 | static int iwm_assoc(struct iwm_softc *); |
414 | static int iwm_release(struct iwm_softc *, struct iwm_node *); | | 414 | static int iwm_release(struct iwm_softc *, struct iwm_node *); |
415 | static void iwm_calib_timeout(void *); | | 415 | static void iwm_calib_timeout(void *); |
416 | static void iwm_setrates(struct iwm_node *); | | 416 | static void iwm_setrates(struct iwm_node *); |
417 | static int iwm_media_change(struct ifnet *); | | 417 | static int iwm_media_change(struct ifnet *); |
418 | static void iwm_newstate_cb(struct work *, void *); | | 418 | static void iwm_newstate_cb(struct work *, void *); |
419 | static int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); | | 419 | static int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); |
420 | static void iwm_endscan_cb(struct work *, void *); | | 420 | static void iwm_endscan_cb(struct work *, void *); |
421 | static int iwm_init_hw(struct iwm_softc *); | | 421 | static int iwm_init_hw(struct iwm_softc *); |
422 | static int iwm_init(struct ifnet *); | | 422 | static int iwm_init(struct ifnet *); |
423 | static void iwm_start(struct ifnet *); | | 423 | static void iwm_start(struct ifnet *); |
424 | static void iwm_stop(struct ifnet *, int); | | 424 | static void iwm_stop(struct ifnet *, int); |
425 | static void iwm_watchdog(struct ifnet *); | | 425 | static void iwm_watchdog(struct ifnet *); |
426 | static int iwm_ioctl(struct ifnet *, u_long, void *); | | 426 | static int iwm_ioctl(struct ifnet *, u_long, void *); |
427 | #ifdef IWM_DEBUG | | 427 | #ifdef IWM_DEBUG |
428 | static const char *iwm_desc_lookup(uint32_t); | | 428 | static const char *iwm_desc_lookup(uint32_t); |
429 | static void iwm_nic_error(struct iwm_softc *); | | 429 | static void iwm_nic_error(struct iwm_softc *); |
430 | #endif | | 430 | #endif |
431 | static void iwm_notif_intr(struct iwm_softc *); | | 431 | static void iwm_notif_intr(struct iwm_softc *); |
432 | static int iwm_intr(void *); | | 432 | static int iwm_intr(void *); |
433 | static int iwm_preinit(struct iwm_softc *); | | 433 | static int iwm_preinit(struct iwm_softc *); |
434 | static void iwm_attach_hook(device_t); | | 434 | static void iwm_attach_hook(device_t); |
435 | static void iwm_attach(device_t, device_t, void *); | | 435 | static void iwm_attach(device_t, device_t, void *); |
436 | #if 0 | | 436 | #if 0 |
437 | static void iwm_init_task(void *); | | 437 | static void iwm_init_task(void *); |
438 | static int iwm_activate(device_t, enum devact); | | 438 | static int iwm_activate(device_t, enum devact); |
439 | static void iwm_wakeup(struct iwm_softc *); | | 439 | static void iwm_wakeup(struct iwm_softc *); |
440 | #endif | | 440 | #endif |
441 | static void iwm_radiotap_attach(struct iwm_softc *); | | 441 | static void iwm_radiotap_attach(struct iwm_softc *); |
442 | | | 442 | |
443 | static int | | 443 | static int |
444 | iwm_firmload(struct iwm_softc *sc) | | 444 | iwm_firmload(struct iwm_softc *sc) |
445 | { | | 445 | { |
446 | struct iwm_fw_info *fw = &sc->sc_fw; | | 446 | struct iwm_fw_info *fw = &sc->sc_fw; |
447 | firmware_handle_t fwh; | | 447 | firmware_handle_t fwh; |
448 | int error; | | 448 | int error; |
449 | | | 449 | |
450 | /* Open firmware image. */ | | 450 | /* Open firmware image. */ |
451 | if ((error = firmware_open("if_iwm", sc->sc_fwname, &fwh)) != 0) { | | 451 | if ((error = firmware_open("if_iwm", sc->sc_fwname, &fwh)) != 0) { |
452 | aprint_error_dev(sc->sc_dev, | | 452 | aprint_error_dev(sc->sc_dev, |
453 | "could not get firmware handle %s\n", sc->sc_fwname); | | 453 | "could not get firmware handle %s\n", sc->sc_fwname); |
454 | return error; | | 454 | return error; |
455 | } | | 455 | } |
456 | | | 456 | |
457 | fw->fw_rawsize = firmware_get_size(fwh); | | 457 | fw->fw_rawsize = firmware_get_size(fwh); |
458 | /* | | 458 | /* |
459 | * Well, this is how the Linux driver checks it .... | | 459 | * Well, this is how the Linux driver checks it .... |
460 | */ | | 460 | */ |
461 | if (fw->fw_rawsize < sizeof(uint32_t)) { | | 461 | if (fw->fw_rawsize < sizeof(uint32_t)) { |
462 | aprint_error_dev(sc->sc_dev, | | 462 | aprint_error_dev(sc->sc_dev, |
463 | "firmware too short: %zd bytes\n", fw->fw_rawsize); | | 463 | "firmware too short: %zd bytes\n", fw->fw_rawsize); |
464 | error = EINVAL; | | 464 | error = EINVAL; |
465 | goto out; | | 465 | goto out; |
466 | } | | 466 | } |
467 | | | 467 | |
468 | /* some sanity */ | | 468 | /* some sanity */ |
469 | if (fw->fw_rawsize > IWM_FWMAXSIZE) { | | 469 | if (fw->fw_rawsize > IWM_FWMAXSIZE) { |
470 | aprint_error_dev(sc->sc_dev, | | 470 | aprint_error_dev(sc->sc_dev, |
471 | "firmware size is ridiculous: %zd bytes\n", | | 471 | "firmware size is ridiculous: %zd bytes\n", |
472 | fw->fw_rawsize); | | 472 | fw->fw_rawsize); |
473 | error = EINVAL; | | 473 | error = EINVAL; |
474 | goto out; | | 474 | goto out; |
475 | } | | 475 | } |
476 | | | 476 | |
477 | /* Read the firmware. */ | | 477 | /* Read the firmware. */ |
478 | fw->fw_rawdata = kmem_alloc(fw->fw_rawsize, KM_SLEEP); | | 478 | fw->fw_rawdata = kmem_alloc(fw->fw_rawsize, KM_SLEEP); |
479 | if (fw->fw_rawdata == NULL) { | | 479 | if (fw->fw_rawdata == NULL) { |
480 | aprint_error_dev(sc->sc_dev, | | 480 | aprint_error_dev(sc->sc_dev, |
481 | "not enough memory to stock firmware %s\n", sc->sc_fwname); | | 481 | "not enough memory to stock firmware %s\n", sc->sc_fwname); |
482 | error = ENOMEM; | | 482 | error = ENOMEM; |
483 | goto out; | | 483 | goto out; |
484 | } | | 484 | } |
485 | error = firmware_read(fwh, 0, fw->fw_rawdata, fw->fw_rawsize); | | 485 | error = firmware_read(fwh, 0, fw->fw_rawdata, fw->fw_rawsize); |
486 | if (error) { | | 486 | if (error) { |
487 | aprint_error_dev(sc->sc_dev, | | 487 | aprint_error_dev(sc->sc_dev, |
488 | "could not read firmware %s\n", sc->sc_fwname); | | 488 | "could not read firmware %s\n", sc->sc_fwname); |
489 | goto out; | | 489 | goto out; |
490 | } | | 490 | } |
491 | | | 491 | |
492 | out: | | 492 | out: |
493 | /* caller will release memory, if necessary */ | | 493 | /* caller will release memory, if necessary */ |
494 | | | 494 | |
495 | firmware_close(fwh); | | 495 | firmware_close(fwh); |
496 | return error; | | 496 | return error; |
497 | } | | 497 | } |
498 | | | 498 | |
499 | /* | | 499 | /* |
500 | * just maintaining status quo. | | 500 | * just maintaining status quo. |
501 | */ | | 501 | */ |
502 | static void | | 502 | static void |
503 | iwm_fix_channel(struct ieee80211com *ic, struct mbuf *m) | | 503 | iwm_fix_channel(struct ieee80211com *ic, struct mbuf *m) |
504 | { | | 504 | { |
505 | struct ieee80211_frame *wh; | | 505 | struct ieee80211_frame *wh; |
506 | uint8_t subtype; | | 506 | uint8_t subtype; |
507 | uint8_t *frm, *efrm; | | 507 | uint8_t *frm, *efrm; |
508 | | | 508 | |
509 | wh = mtod(m, struct ieee80211_frame *); | | 509 | wh = mtod(m, struct ieee80211_frame *); |
510 | | | 510 | |
511 | if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) | | 511 | if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) |
512 | return; | | 512 | return; |
513 | | | 513 | |
514 | subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; | | 514 | subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
515 | | | 515 | |
516 | if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && | | 516 | if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && |
517 | subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) | | 517 | subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) |
518 | return; | | 518 | return; |
519 | | | 519 | |
520 | frm = (uint8_t *)(wh + 1); | | 520 | frm = (uint8_t *)(wh + 1); |
521 | efrm = mtod(m, uint8_t *) + m->m_len; | | 521 | efrm = mtod(m, uint8_t *) + m->m_len; |
522 | | | 522 | |
523 | frm += 12; /* skip tstamp, bintval and capinfo fields */ | | 523 | frm += 12; /* skip tstamp, bintval and capinfo fields */ |
524 | while (frm < efrm) { | | 524 | while (frm < efrm) { |
525 | if (*frm == IEEE80211_ELEMID_DSPARMS) { | | 525 | if (*frm == IEEE80211_ELEMID_DSPARMS) { |
526 | #if IEEE80211_CHAN_MAX < 255 | | 526 | #if IEEE80211_CHAN_MAX < 255 |
527 | if (frm[2] <= IEEE80211_CHAN_MAX) | | 527 | if (frm[2] <= IEEE80211_CHAN_MAX) |
528 | #endif | | 528 | #endif |
529 | ic->ic_curchan = &ic->ic_channels[frm[2]]; | | 529 | ic->ic_curchan = &ic->ic_channels[frm[2]]; |
530 | } | | 530 | } |
531 | frm += frm[1] + 2; | | 531 | frm += frm[1] + 2; |
532 | } | | 532 | } |
533 | } | | 533 | } |
534 | | | 534 | |
535 | /* | | 535 | /* |
536 | * Firmware parser. | | 536 | * Firmware parser. |
537 | */ | | 537 | */ |
538 | | | 538 | |
539 | static int | | 539 | static int |
540 | iwm_store_cscheme(struct iwm_softc *sc, uint8_t *data, size_t dlen) | | 540 | iwm_store_cscheme(struct iwm_softc *sc, uint8_t *data, size_t dlen) |
541 | { | | 541 | { |
542 | struct iwm_fw_cscheme_list *l = (void *)data; | | 542 | struct iwm_fw_cscheme_list *l = (void *)data; |
543 | | | 543 | |
544 | if (dlen < sizeof(*l) || | | 544 | if (dlen < sizeof(*l) || |
545 | dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) | | 545 | dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) |
546 | return EINVAL; | | 546 | return EINVAL; |
547 | | | 547 | |
548 | /* we don't actually store anything for now, always use s/w crypto */ | | 548 | /* we don't actually store anything for now, always use s/w crypto */ |
549 | | | 549 | |
550 | return 0; | | 550 | return 0; |
551 | } | | 551 | } |
552 | | | 552 | |
553 | static int | | 553 | static int |
554 | iwm_firmware_store_section(struct iwm_softc *sc, | | 554 | iwm_firmware_store_section(struct iwm_softc *sc, |
555 | enum iwm_ucode_type type, uint8_t *data, size_t dlen) | | 555 | enum iwm_ucode_type type, uint8_t *data, size_t dlen) |
556 | { | | 556 | { |
557 | struct iwm_fw_sects *fws; | | 557 | struct iwm_fw_sects *fws; |
558 | struct iwm_fw_onesect *fwone; | | 558 | struct iwm_fw_onesect *fwone; |
559 | | | 559 | |
560 | if (type >= IWM_UCODE_TYPE_MAX) | | 560 | if (type >= IWM_UCODE_TYPE_MAX) |
561 | return EINVAL; | | 561 | return EINVAL; |
562 | if (dlen < sizeof(uint32_t)) | | 562 | if (dlen < sizeof(uint32_t)) |
563 | return EINVAL; | | 563 | return EINVAL; |
564 | | | 564 | |
565 | fws = &sc->sc_fw.fw_sects[type]; | | 565 | fws = &sc->sc_fw.fw_sects[type]; |
566 | if (fws->fw_count >= IWM_UCODE_SECT_MAX) | | 566 | if (fws->fw_count >= IWM_UCODE_SECT_MAX) |
567 | return EINVAL; | | 567 | return EINVAL; |
568 | | | 568 | |
569 | fwone = &fws->fw_sect[fws->fw_count]; | | 569 | fwone = &fws->fw_sect[fws->fw_count]; |
570 | | | 570 | |
571 | /* first 32bit are device load offset */ | | 571 | /* first 32bit are device load offset */ |
572 | memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); | | 572 | memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); |
573 | | | 573 | |
574 | /* rest is data */ | | 574 | /* rest is data */ |
575 | fwone->fws_data = data + sizeof(uint32_t); | | 575 | fwone->fws_data = data + sizeof(uint32_t); |
576 | fwone->fws_len = dlen - sizeof(uint32_t); | | 576 | fwone->fws_len = dlen - sizeof(uint32_t); |
577 | | | 577 | |
578 | /* for freeing the buffer during driver unload */ | | 578 | /* for freeing the buffer during driver unload */ |
579 | fwone->fws_alloc = data; | | 579 | fwone->fws_alloc = data; |
580 | fwone->fws_allocsize = dlen; | | 580 | fwone->fws_allocsize = dlen; |
581 | | | 581 | |
582 | fws->fw_count++; | | 582 | fws->fw_count++; |
583 | fws->fw_totlen += fwone->fws_len; | | 583 | fws->fw_totlen += fwone->fws_len; |
584 | | | 584 | |
585 | return 0; | | 585 | return 0; |
586 | } | | 586 | } |
587 | | | 587 | |
588 | /* iwlwifi: iwl-drv.c */ | | 588 | /* iwlwifi: iwl-drv.c */ |
589 | struct iwm_tlv_calib_data { | | 589 | struct iwm_tlv_calib_data { |
590 | uint32_t ucode_type; | | 590 | uint32_t ucode_type; |
591 | struct iwm_tlv_calib_ctrl calib; | | 591 | struct iwm_tlv_calib_ctrl calib; |
592 | } __packed; | | 592 | } __packed; |
593 | | | 593 | |
594 | static int | | 594 | static int |
595 | iwm_set_default_calib(struct iwm_softc *sc, const void *data) | | 595 | iwm_set_default_calib(struct iwm_softc *sc, const void *data) |
596 | { | | 596 | { |
597 | const struct iwm_tlv_calib_data *def_calib = data; | | 597 | const struct iwm_tlv_calib_data *def_calib = data; |
598 | uint32_t ucode_type = le32toh(def_calib->ucode_type); | | 598 | uint32_t ucode_type = le32toh(def_calib->ucode_type); |
599 | | | 599 | |
600 | if (ucode_type >= IWM_UCODE_TYPE_MAX) { | | 600 | if (ucode_type >= IWM_UCODE_TYPE_MAX) { |
601 | DPRINTF(("%s: Wrong ucode_type %u for default " | | 601 | DPRINTF(("%s: Wrong ucode_type %u for default " |
602 | "calibration.\n", DEVNAME(sc), ucode_type)); | | 602 | "calibration.\n", DEVNAME(sc), ucode_type)); |
603 | return EINVAL; | | 603 | return EINVAL; |
604 | } | | 604 | } |
605 | | | 605 | |
606 | sc->sc_default_calib[ucode_type].flow_trigger = | | 606 | sc->sc_default_calib[ucode_type].flow_trigger = |
607 | def_calib->calib.flow_trigger; | | 607 | def_calib->calib.flow_trigger; |
608 | sc->sc_default_calib[ucode_type].event_trigger = | | 608 | sc->sc_default_calib[ucode_type].event_trigger = |
609 | def_calib->calib.event_trigger; | | 609 | def_calib->calib.event_trigger; |
610 | | | 610 | |
611 | return 0; | | 611 | return 0; |
612 | } | | 612 | } |
613 | | | 613 | |
614 | static int | | 614 | static int |
615 | iwm_read_firmware(struct iwm_softc *sc) | | 615 | iwm_read_firmware(struct iwm_softc *sc) |
616 | { | | 616 | { |
617 | struct iwm_fw_info *fw = &sc->sc_fw; | | 617 | struct iwm_fw_info *fw = &sc->sc_fw; |
618 | struct iwm_tlv_ucode_header *uhdr; | | 618 | struct iwm_tlv_ucode_header *uhdr; |
619 | struct iwm_ucode_tlv tlv; | | 619 | struct iwm_ucode_tlv tlv; |
620 | enum iwm_ucode_tlv_type tlv_type; | | 620 | enum iwm_ucode_tlv_type tlv_type; |
621 | uint8_t *data; | | 621 | uint8_t *data; |
622 | int error, status; | | 622 | int error, status; |
623 | size_t len; | | 623 | size_t len; |
624 | | | 624 | |
625 | if (fw->fw_status == IWM_FW_STATUS_NONE) { | | 625 | if (fw->fw_status == IWM_FW_STATUS_NONE) { |
626 | fw->fw_status = IWM_FW_STATUS_INPROGRESS; | | 626 | fw->fw_status = IWM_FW_STATUS_INPROGRESS; |
627 | } else { | | 627 | } else { |
628 | while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) | | 628 | while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) |
629 | tsleep(&sc->sc_fw, 0, "iwmfwp", 0); | | 629 | tsleep(&sc->sc_fw, 0, "iwmfwp", 0); |
630 | } | | 630 | } |
631 | status = fw->fw_status; | | 631 | status = fw->fw_status; |
632 | | | 632 | |
633 | if (status == IWM_FW_STATUS_DONE) | | 633 | if (status == IWM_FW_STATUS_DONE) |
634 | return 0; | | 634 | return 0; |
635 | | | 635 | |
636 | /* | | 636 | /* |
637 | * Load firmware into driver memory. | | 637 | * Load firmware into driver memory. |
638 | * fw_rawdata and fw_rawsize will be set. | | 638 | * fw_rawdata and fw_rawsize will be set. |
639 | */ | | 639 | */ |
640 | error = iwm_firmload(sc); | | 640 | error = iwm_firmload(sc); |
641 | if (error != 0) { | | 641 | if (error != 0) { |
642 | aprint_error_dev(sc->sc_dev, | | 642 | aprint_error_dev(sc->sc_dev, |
643 | "could not read firmware %s (error %d)\n", | | 643 | "could not read firmware %s (error %d)\n", |
644 | sc->sc_fwname, error); | | 644 | sc->sc_fwname, error); |
645 | goto out; | | 645 | goto out; |
646 | } | | 646 | } |
647 | | | 647 | |
648 | /* | | 648 | /* |
649 | * Parse firmware contents | | 649 | * Parse firmware contents |
650 | */ | | 650 | */ |
651 | | | 651 | |
652 | uhdr = (void *)fw->fw_rawdata; | | 652 | uhdr = (void *)fw->fw_rawdata; |
653 | if (*(uint32_t *)fw->fw_rawdata != 0 | | 653 | if (*(uint32_t *)fw->fw_rawdata != 0 |
654 | || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { | | 654 | || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { |
655 | aprint_error_dev(sc->sc_dev, "invalid firmware %s\n", | | 655 | aprint_error_dev(sc->sc_dev, "invalid firmware %s\n", |
656 | sc->sc_fwname); | | 656 | sc->sc_fwname); |
657 | error = EINVAL; | | 657 | error = EINVAL; |
658 | goto out; | | 658 | goto out; |
659 | } | | 659 | } |
660 | | | 660 | |
661 | sc->sc_fwver = le32toh(uhdr->ver); | | 661 | sc->sc_fwver = le32toh(uhdr->ver); |
662 | data = uhdr->data; | | 662 | data = uhdr->data; |
663 | len = fw->fw_rawsize - sizeof(*uhdr); | | 663 | len = fw->fw_rawsize - sizeof(*uhdr); |
664 | | | 664 | |
665 | while (len >= sizeof(tlv)) { | | 665 | while (len >= sizeof(tlv)) { |
666 | size_t tlv_len; | | 666 | size_t tlv_len; |
667 | void *tlv_data; | | 667 | void *tlv_data; |
668 | | | 668 | |
669 | memcpy(&tlv, data, sizeof(tlv)); | | 669 | memcpy(&tlv, data, sizeof(tlv)); |
670 | tlv_len = le32toh(tlv.length); | | 670 | tlv_len = le32toh(tlv.length); |
671 | tlv_type = le32toh(tlv.type); | | 671 | tlv_type = le32toh(tlv.type); |
672 | | | 672 | |
673 | len -= sizeof(tlv); | | 673 | len -= sizeof(tlv); |
674 | data += sizeof(tlv); | | 674 | data += sizeof(tlv); |
675 | tlv_data = data; | | 675 | tlv_data = data; |
676 | | | 676 | |
677 | if (len < tlv_len) { | | 677 | if (len < tlv_len) { |
678 | aprint_error_dev(sc->sc_dev, | | 678 | aprint_error_dev(sc->sc_dev, |
679 | "firmware too short: %zu bytes\n", len); | | 679 | "firmware too short: %zu bytes\n", len); |
680 | error = EINVAL; | | 680 | error = EINVAL; |
681 | goto parse_out; | | 681 | goto parse_out; |
682 | } | | 682 | } |
683 | | | 683 | |
684 | switch ((int)tlv_type) { | | 684 | switch ((int)tlv_type) { |
685 | case IWM_UCODE_TLV_PROBE_MAX_LEN: | | 685 | case IWM_UCODE_TLV_PROBE_MAX_LEN: |
686 | if (tlv_len < sizeof(uint32_t)) { | | 686 | if (tlv_len < sizeof(uint32_t)) { |
687 | error = EINVAL; | | 687 | error = EINVAL; |
688 | goto parse_out; | | 688 | goto parse_out; |
689 | } | | 689 | } |
690 | sc->sc_capa_max_probe_len | | 690 | sc->sc_capa_max_probe_len |
691 | = le32toh(*(uint32_t *)tlv_data); | | 691 | = le32toh(*(uint32_t *)tlv_data); |
692 | /* limit it to something sensible */ | | 692 | /* limit it to something sensible */ |
693 | if (sc->sc_capa_max_probe_len > (1<<16)) { | | 693 | if (sc->sc_capa_max_probe_len > (1<<16)) { |
694 | DPRINTF(("%s: IWM_UCODE_TLV_PROBE_MAX_LEN " | | 694 | DPRINTF(("%s: IWM_UCODE_TLV_PROBE_MAX_LEN " |
695 | "ridiculous\n", DEVNAME(sc))); | | 695 | "ridiculous\n", DEVNAME(sc))); |
696 | error = EINVAL; | | 696 | error = EINVAL; |
697 | goto parse_out; | | 697 | goto parse_out; |
698 | } | | 698 | } |
699 | break; | | 699 | break; |
700 | case IWM_UCODE_TLV_PAN: | | 700 | case IWM_UCODE_TLV_PAN: |
701 | if (tlv_len) { | | 701 | if (tlv_len) { |
702 | error = EINVAL; | | 702 | error = EINVAL; |
703 | goto parse_out; | | 703 | goto parse_out; |
704 | } | | 704 | } |
705 | sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; | | 705 | sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; |
706 | break; | | 706 | break; |
707 | case IWM_UCODE_TLV_FLAGS: | | 707 | case IWM_UCODE_TLV_FLAGS: |
708 | if (tlv_len < sizeof(uint32_t)) { | | 708 | if (tlv_len < sizeof(uint32_t)) { |
709 | error = EINVAL; | | 709 | error = EINVAL; |
710 | goto parse_out; | | 710 | goto parse_out; |
711 | } | | 711 | } |
712 | /* | | 712 | /* |
713 | * Apparently there can be many flags, but Linux driver | | 713 | * Apparently there can be many flags, but Linux driver |
714 | * parses only the first one, and so do we. | | 714 | * parses only the first one, and so do we. |
715 | * | | 715 | * |
716 | * XXX: why does this override IWM_UCODE_TLV_PAN? | | 716 | * XXX: why does this override IWM_UCODE_TLV_PAN? |
717 | * Intentional or a bug? Observations from | | 717 | * Intentional or a bug? Observations from |
718 | * current firmware file: | | 718 | * current firmware file: |
719 | * 1) TLV_PAN is parsed first | | 719 | * 1) TLV_PAN is parsed first |
720 | * 2) TLV_FLAGS contains TLV_FLAGS_PAN | | 720 | * 2) TLV_FLAGS contains TLV_FLAGS_PAN |
721 | * ==> this resets TLV_PAN to itself... hnnnk | | 721 | * ==> this resets TLV_PAN to itself... hnnnk |
722 | */ | | 722 | */ |
723 | sc->sc_capaflags = le32toh(*(uint32_t *)tlv_data); | | 723 | sc->sc_capaflags = le32toh(*(uint32_t *)tlv_data); |
724 | break; | | 724 | break; |
725 | case IWM_UCODE_TLV_CSCHEME: | | 725 | case IWM_UCODE_TLV_CSCHEME: |
726 | if ((error = iwm_store_cscheme(sc, | | 726 | if ((error = iwm_store_cscheme(sc, |
727 | tlv_data, tlv_len)) != 0) | | 727 | tlv_data, tlv_len)) != 0) |
728 | goto parse_out; | | 728 | goto parse_out; |
729 | break; | | 729 | break; |
730 | case IWM_UCODE_TLV_NUM_OF_CPU: | | 730 | case IWM_UCODE_TLV_NUM_OF_CPU: |
731 | if (tlv_len != sizeof(uint32_t)) { | | 731 | if (tlv_len != sizeof(uint32_t)) { |
732 | error = EINVAL; | | 732 | error = EINVAL; |
733 | goto parse_out; | | 733 | goto parse_out; |
734 | } | | 734 | } |
735 | if (le32toh(*(uint32_t*)tlv_data) != 1) { | | 735 | if (le32toh(*(uint32_t*)tlv_data) != 1) { |
736 | DPRINTF(("%s: driver supports " | | 736 | DPRINTF(("%s: driver supports " |
737 | "only TLV_NUM_OF_CPU == 1", DEVNAME(sc))); | | 737 | "only TLV_NUM_OF_CPU == 1", DEVNAME(sc))); |
738 | error = EINVAL; | | 738 | error = EINVAL; |
739 | goto parse_out; | | 739 | goto parse_out; |
740 | } | | 740 | } |
741 | break; | | 741 | break; |
742 | case IWM_UCODE_TLV_SEC_RT: | | 742 | case IWM_UCODE_TLV_SEC_RT: |
743 | if ((error = iwm_firmware_store_section(sc, | | 743 | if ((error = iwm_firmware_store_section(sc, |
744 | IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) | | 744 | IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) |
745 | goto parse_out; | | 745 | goto parse_out; |
746 | break; | | 746 | break; |
747 | case IWM_UCODE_TLV_SEC_INIT: | | 747 | case IWM_UCODE_TLV_SEC_INIT: |
748 | if ((error = iwm_firmware_store_section(sc, | | 748 | if ((error = iwm_firmware_store_section(sc, |
749 | IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) | | 749 | IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) |
750 | goto parse_out; | | 750 | goto parse_out; |
751 | break; | | 751 | break; |
752 | case IWM_UCODE_TLV_SEC_WOWLAN: | | 752 | case IWM_UCODE_TLV_SEC_WOWLAN: |
753 | if ((error = iwm_firmware_store_section(sc, | | 753 | if ((error = iwm_firmware_store_section(sc, |
754 | IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) | | 754 | IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) |
755 | goto parse_out; | | 755 | goto parse_out; |
756 | break; | | 756 | break; |
757 | case IWM_UCODE_TLV_DEF_CALIB: | | 757 | case IWM_UCODE_TLV_DEF_CALIB: |
758 | if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { | | 758 | if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { |
759 | error = EINVAL; | | 759 | error = EINVAL; |
760 | goto parse_out; | | 760 | goto parse_out; |
761 | } | | 761 | } |
762 | if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) | | 762 | if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) |
763 | goto parse_out; | | 763 | goto parse_out; |
764 | break; | | 764 | break; |
765 | case IWM_UCODE_TLV_PHY_SKU: | | 765 | case IWM_UCODE_TLV_PHY_SKU: |
766 | if (tlv_len != sizeof(uint32_t)) { | | 766 | if (tlv_len != sizeof(uint32_t)) { |
767 | error = EINVAL; | | 767 | error = EINVAL; |
768 | goto parse_out; | | 768 | goto parse_out; |
769 | } | | 769 | } |
770 | sc->sc_fw_phy_config = le32toh(*(uint32_t *)tlv_data); | | 770 | sc->sc_fw_phy_config = le32toh(*(uint32_t *)tlv_data); |
771 | break; | | 771 | break; |
772 | | | 772 | |
773 | case IWM_UCODE_TLV_API_CHANGES_SET: | | 773 | case IWM_UCODE_TLV_API_CHANGES_SET: |
774 | case IWM_UCODE_TLV_ENABLED_CAPABILITIES: | | 774 | case IWM_UCODE_TLV_ENABLED_CAPABILITIES: |
775 | /* ignore, not used by current driver */ | | 775 | /* ignore, not used by current driver */ |
776 | break; | | 776 | break; |
777 | | | 777 | |
778 | default: | | 778 | default: |
779 | DPRINTF(("%s: unknown firmware section %d, abort\n", | | 779 | DPRINTF(("%s: unknown firmware section %d, abort\n", |
780 | DEVNAME(sc), tlv_type)); | | 780 | DEVNAME(sc), tlv_type)); |
781 | error = EINVAL; | | 781 | error = EINVAL; |
782 | goto parse_out; | | 782 | goto parse_out; |
783 | } | | 783 | } |
784 | | | 784 | |
785 | len -= roundup(tlv_len, 4); | | 785 | len -= roundup(tlv_len, 4); |
786 | data += roundup(tlv_len, 4); | | 786 | data += roundup(tlv_len, 4); |
787 | } | | 787 | } |
788 | | | 788 | |
789 | KASSERT(error == 0); | | 789 | KASSERT(error == 0); |
790 | | | 790 | |
791 | parse_out: | | 791 | parse_out: |
792 | if (error) { | | 792 | if (error) { |
793 | aprint_error_dev(sc->sc_dev, | | 793 | aprint_error_dev(sc->sc_dev, |
794 | "firmware parse error, section type %d\n", tlv_type); | | 794 | "firmware parse error, section type %d\n", tlv_type); |
795 | } | | 795 | } |
796 | | | 796 | |
797 | if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { | | 797 | if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { |
798 | aprint_error_dev(sc->sc_dev, | | 798 | aprint_error_dev(sc->sc_dev, |
799 | "device uses unsupported power ops\n"); | | 799 | "device uses unsupported power ops\n"); |
800 | error = ENOTSUP; | | 800 | error = ENOTSUP; |
801 | } | | 801 | } |
802 | | | 802 | |
803 | out: | | 803 | out: |
804 | if (error) | | 804 | if (error) |
805 | fw->fw_status = IWM_FW_STATUS_NONE; | | 805 | fw->fw_status = IWM_FW_STATUS_NONE; |
806 | else | | 806 | else |
807 | fw->fw_status = IWM_FW_STATUS_DONE; | | 807 | fw->fw_status = IWM_FW_STATUS_DONE; |
808 | wakeup(&sc->sc_fw); | | 808 | wakeup(&sc->sc_fw); |
809 | | | 809 | |
810 | if (error) { | | 810 | if (error) { |
811 | kmem_free(fw->fw_rawdata, fw->fw_rawsize); | | 811 | kmem_free(fw->fw_rawdata, fw->fw_rawsize); |
812 | fw->fw_rawdata = NULL; | | 812 | fw->fw_rawdata = NULL; |
813 | } | | 813 | } |
814 | return error; | | 814 | return error; |
815 | } | | 815 | } |
816 | | | 816 | |
817 | /* | | 817 | /* |
818 | * basic device access | | 818 | * basic device access |
819 | */ | | 819 | */ |
820 | | | 820 | |
821 | static uint32_t | | 821 | static uint32_t |
822 | iwm_read_prph(struct iwm_softc *sc, uint32_t addr) | | 822 | iwm_read_prph(struct iwm_softc *sc, uint32_t addr) |
823 | { | | 823 | { |
824 | IWM_WRITE(sc, | | 824 | IWM_WRITE(sc, |
825 | IWM_HBUS_TARG_PRPH_RADDR, ((addr & 0x000fffff) | (3 << 24))); | | 825 | IWM_HBUS_TARG_PRPH_RADDR, ((addr & 0x000fffff) | (3 << 24))); |
826 | IWM_BARRIER_READ_WRITE(sc); | | 826 | IWM_BARRIER_READ_WRITE(sc); |
827 | return IWM_READ(sc, IWM_HBUS_TARG_PRPH_RDAT); | | 827 | return IWM_READ(sc, IWM_HBUS_TARG_PRPH_RDAT); |
828 | } | | 828 | } |
829 | | | 829 | |
830 | static void | | 830 | static void |
831 | iwm_write_prph(struct iwm_softc *sc, uint32_t addr, uint32_t val) | | 831 | iwm_write_prph(struct iwm_softc *sc, uint32_t addr, uint32_t val) |
832 | { | | 832 | { |
833 | IWM_WRITE(sc, | | 833 | IWM_WRITE(sc, |
834 | IWM_HBUS_TARG_PRPH_WADDR, ((addr & 0x000fffff) | (3 << 24))); | | 834 | IWM_HBUS_TARG_PRPH_WADDR, ((addr & 0x000fffff) | (3 << 24))); |
835 | IWM_BARRIER_WRITE(sc); | | 835 | IWM_BARRIER_WRITE(sc); |
836 | IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WDAT, val); | | 836 | IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WDAT, val); |
837 | } | | 837 | } |
838 | | | 838 | |
839 | #ifdef IWM_DEBUG | | 839 | #ifdef IWM_DEBUG |
840 | /* iwlwifi: pcie/trans.c */ | | 840 | /* iwlwifi: pcie/trans.c */ |
841 | static int | | 841 | static int |
842 | iwm_read_mem(struct iwm_softc *sc, uint32_t addr, void *buf, int dwords) | | 842 | iwm_read_mem(struct iwm_softc *sc, uint32_t addr, void *buf, int dwords) |
843 | { | | 843 | { |
844 | int offs, ret = 0; | | 844 | int offs, ret = 0; |
845 | uint32_t *vals = buf; | | 845 | uint32_t *vals = buf; |
846 | | | 846 | |
847 | if (iwm_nic_lock(sc)) { | | 847 | if (iwm_nic_lock(sc)) { |
848 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_RADDR, addr); | | 848 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_RADDR, addr); |
849 | for (offs = 0; offs < dwords; offs++) | | 849 | for (offs = 0; offs < dwords; offs++) |
850 | vals[offs] = IWM_READ(sc, IWM_HBUS_TARG_MEM_RDAT); | | 850 | vals[offs] = IWM_READ(sc, IWM_HBUS_TARG_MEM_RDAT); |
851 | iwm_nic_unlock(sc); | | 851 | iwm_nic_unlock(sc); |
852 | } else { | | 852 | } else { |
853 | ret = EBUSY; | | 853 | ret = EBUSY; |
854 | } | | 854 | } |
855 | return ret; | | 855 | return ret; |
856 | } | | 856 | } |
857 | #endif | | 857 | #endif |
858 | | | 858 | |
859 | /* iwlwifi: pcie/trans.c */ | | 859 | /* iwlwifi: pcie/trans.c */ |
860 | static int | | 860 | static int |
861 | iwm_write_mem(struct iwm_softc *sc, uint32_t addr, const void *buf, int dwords) | | 861 | iwm_write_mem(struct iwm_softc *sc, uint32_t addr, const void *buf, int dwords) |
862 | { | | 862 | { |
863 | int offs; | | 863 | int offs; |
864 | const uint32_t *vals = buf; | | 864 | const uint32_t *vals = buf; |
865 | | | 865 | |
866 | if (iwm_nic_lock(sc)) { | | 866 | if (iwm_nic_lock(sc)) { |
867 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WADDR, addr); | | 867 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WADDR, addr); |
868 | /* WADDR auto-increments */ | | 868 | /* WADDR auto-increments */ |
869 | for (offs = 0; offs < dwords; offs++) { | | 869 | for (offs = 0; offs < dwords; offs++) { |
870 | uint32_t val = vals ? vals[offs] : 0; | | 870 | uint32_t val = vals ? vals[offs] : 0; |
871 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WDAT, val); | | 871 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WDAT, val); |
872 | } | | 872 | } |
873 | iwm_nic_unlock(sc); | | 873 | iwm_nic_unlock(sc); |
874 | } else { | | 874 | } else { |
875 | DPRINTF(("%s: write_mem failed\n", DEVNAME(sc))); | | 875 | DPRINTF(("%s: write_mem failed\n", DEVNAME(sc))); |
876 | return EBUSY; | | 876 | return EBUSY; |
877 | } | | 877 | } |
878 | return 0; | | 878 | return 0; |
879 | } | | 879 | } |
880 | | | 880 | |
881 | static int | | 881 | static int |
882 | iwm_write_mem32(struct iwm_softc *sc, uint32_t addr, uint32_t val) | | 882 | iwm_write_mem32(struct iwm_softc *sc, uint32_t addr, uint32_t val) |
883 | { | | 883 | { |
884 | return iwm_write_mem(sc, addr, &val, 1); | | 884 | return iwm_write_mem(sc, addr, &val, 1); |
885 | } | | 885 | } |
886 | | | 886 | |
887 | static int | | 887 | static int |
888 | iwm_poll_bit(struct iwm_softc *sc, int reg, | | 888 | iwm_poll_bit(struct iwm_softc *sc, int reg, |
889 | uint32_t bits, uint32_t mask, int timo) | | 889 | uint32_t bits, uint32_t mask, int timo) |
890 | { | | 890 | { |
891 | for (;;) { | | 891 | for (;;) { |
892 | if ((IWM_READ(sc, reg) & mask) == (bits & mask)) { | | 892 | if ((IWM_READ(sc, reg) & mask) == (bits & mask)) { |
893 | return 1; | | 893 | return 1; |
894 | } | | 894 | } |
895 | if (timo < 10) { | | 895 | if (timo < 10) { |
896 | return 0; | | 896 | return 0; |
897 | } | | 897 | } |
898 | timo -= 10; | | 898 | timo -= 10; |
899 | DELAY(10); | | 899 | DELAY(10); |
900 | } | | 900 | } |
901 | } | | 901 | } |
902 | | | 902 | |
903 | static int | | 903 | static int |
904 | iwm_nic_lock(struct iwm_softc *sc) | | 904 | iwm_nic_lock(struct iwm_softc *sc) |
905 | { | | 905 | { |
906 | int rv = 0; | | 906 | int rv = 0; |
907 | | | 907 | |
908 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, | | 908 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, |
909 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | | 909 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); |
910 | | | 910 | |
911 | if (iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, | | 911 | if (iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, |
912 | IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, | | 912 | IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, |
913 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | | 913 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
914 | | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP, 15000)) { | | 914 | | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP, 15000)) { |
915 | rv = 1; | | 915 | rv = 1; |
916 | } else { | | 916 | } else { |
917 | /* jolt */ | | 917 | /* jolt */ |
918 | IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_FORCE_NMI); | | 918 | IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_FORCE_NMI); |
919 | } | | 919 | } |
920 | | | 920 | |
921 | return rv; | | 921 | return rv; |
922 | } | | 922 | } |
923 | | | 923 | |
924 | static void | | 924 | static void |
925 | iwm_nic_unlock(struct iwm_softc *sc) | | 925 | iwm_nic_unlock(struct iwm_softc *sc) |
926 | { | | 926 | { |
927 | IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, | | 927 | IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, |
928 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | | 928 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); |
929 | } | | 929 | } |
930 | | | 930 | |
931 | static void | | 931 | static void |
932 | iwm_set_bits_mask_prph(struct iwm_softc *sc, | | 932 | iwm_set_bits_mask_prph(struct iwm_softc *sc, |
933 | uint32_t reg, uint32_t bits, uint32_t mask) | | 933 | uint32_t reg, uint32_t bits, uint32_t mask) |
934 | { | | 934 | { |
935 | uint32_t val; | | 935 | uint32_t val; |
936 | | | 936 | |
937 | /* XXX: no error path? */ | | 937 | /* XXX: no error path? */ |
938 | if (iwm_nic_lock(sc)) { | | 938 | if (iwm_nic_lock(sc)) { |
939 | val = iwm_read_prph(sc, reg) & mask; | | 939 | val = iwm_read_prph(sc, reg) & mask; |
940 | val |= bits; | | 940 | val |= bits; |
941 | iwm_write_prph(sc, reg, val); | | 941 | iwm_write_prph(sc, reg, val); |
942 | iwm_nic_unlock(sc); | | 942 | iwm_nic_unlock(sc); |
943 | } | | 943 | } |
944 | } | | 944 | } |
945 | | | 945 | |
946 | static void | | 946 | static void |
947 | iwm_set_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) | | 947 | iwm_set_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) |
948 | { | | 948 | { |
949 | iwm_set_bits_mask_prph(sc, reg, bits, ~0); | | 949 | iwm_set_bits_mask_prph(sc, reg, bits, ~0); |
950 | } | | 950 | } |
951 | | | 951 | |
952 | static void | | 952 | static void |
953 | iwm_clear_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) | | 953 | iwm_clear_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) |
954 | { | | 954 | { |
955 | iwm_set_bits_mask_prph(sc, reg, 0, ~bits); | | 955 | iwm_set_bits_mask_prph(sc, reg, 0, ~bits); |
956 | } | | 956 | } |
957 | | | 957 | |
958 | /* | | 958 | /* |
959 | * DMA resource routines | | 959 | * DMA resource routines |
960 | */ | | 960 | */ |
961 | | | 961 | |
962 | static int | | 962 | static int |
963 | iwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma, | | 963 | iwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma, |
964 | bus_size_t size, bus_size_t alignment) | | 964 | bus_size_t size, bus_size_t alignment) |
965 | { | | 965 | { |
966 | int nsegs, error; | | 966 | int nsegs, error; |
967 | void *va; | | 967 | void *va; |
968 | | | 968 | |
969 | dma->tag = tag; | | 969 | dma->tag = tag; |
970 | dma->size = size; | | 970 | dma->size = size; |
971 | | | 971 | |
972 | error = bus_dmamap_create(tag, size, 1, size, 0, BUS_DMA_NOWAIT, | | 972 | error = bus_dmamap_create(tag, size, 1, size, 0, BUS_DMA_NOWAIT, |
973 | &dma->map); | | 973 | &dma->map); |
974 | if (error != 0) | | 974 | if (error != 0) |
975 | goto fail; | | 975 | goto fail; |
976 | | | 976 | |
977 | error = bus_dmamem_alloc(tag, size, alignment, 0, &dma->seg, 1, &nsegs, | | 977 | error = bus_dmamem_alloc(tag, size, alignment, 0, &dma->seg, 1, &nsegs, |
978 | BUS_DMA_NOWAIT); | | 978 | BUS_DMA_NOWAIT); |
979 | if (error != 0) | | 979 | if (error != 0) |
980 | goto fail; | | 980 | goto fail; |
981 | | | 981 | |
982 | error = bus_dmamem_map(tag, &dma->seg, 1, size, &va, | | 982 | error = bus_dmamem_map(tag, &dma->seg, 1, size, &va, |
983 | BUS_DMA_NOWAIT); | | 983 | BUS_DMA_NOWAIT); |
984 | if (error != 0) | | 984 | if (error != 0) |
985 | goto fail; | | 985 | goto fail; |
986 | dma->vaddr = va; | | 986 | dma->vaddr = va; |
987 | | | 987 | |
988 | error = bus_dmamap_load(tag, dma->map, dma->vaddr, size, NULL, | | 988 | error = bus_dmamap_load(tag, dma->map, dma->vaddr, size, NULL, |
989 | BUS_DMA_NOWAIT); | | 989 | BUS_DMA_NOWAIT); |
990 | if (error != 0) | | 990 | if (error != 0) |
991 | goto fail; | | 991 | goto fail; |
992 | | | 992 | |
993 | memset(dma->vaddr, 0, size); | | 993 | memset(dma->vaddr, 0, size); |
994 | bus_dmamap_sync(tag, dma->map, 0, size, BUS_DMASYNC_PREWRITE); | | 994 | bus_dmamap_sync(tag, dma->map, 0, size, BUS_DMASYNC_PREWRITE); |
995 | dma->paddr = dma->map->dm_segs[0].ds_addr; | | 995 | dma->paddr = dma->map->dm_segs[0].ds_addr; |
996 | | | 996 | |
997 | return 0; | | 997 | return 0; |
998 | | | 998 | |
999 | fail: iwm_dma_contig_free(dma); | | 999 | fail: iwm_dma_contig_free(dma); |
1000 | return error; | | 1000 | return error; |
1001 | } | | 1001 | } |
1002 | | | 1002 | |
1003 | static void | | 1003 | static void |
1004 | iwm_dma_contig_free(struct iwm_dma_info *dma) | | 1004 | iwm_dma_contig_free(struct iwm_dma_info *dma) |
1005 | { | | 1005 | { |
1006 | if (dma->map != NULL) { | | 1006 | if (dma->map != NULL) { |
1007 | if (dma->vaddr != NULL) { | | 1007 | if (dma->vaddr != NULL) { |
1008 | bus_dmamap_sync(dma->tag, dma->map, 0, dma->size, | | 1008 | bus_dmamap_sync(dma->tag, dma->map, 0, dma->size, |
1009 | BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); | | 1009 | BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); |
1010 | bus_dmamap_unload(dma->tag, dma->map); | | 1010 | bus_dmamap_unload(dma->tag, dma->map); |
1011 | bus_dmamem_unmap(dma->tag, dma->vaddr, dma->size); | | 1011 | bus_dmamem_unmap(dma->tag, dma->vaddr, dma->size); |
1012 | bus_dmamem_free(dma->tag, &dma->seg, 1); | | 1012 | bus_dmamem_free(dma->tag, &dma->seg, 1); |
1013 | dma->vaddr = NULL; | | 1013 | dma->vaddr = NULL; |
1014 | } | | 1014 | } |
1015 | bus_dmamap_destroy(dma->tag, dma->map); | | 1015 | bus_dmamap_destroy(dma->tag, dma->map); |
1016 | dma->map = NULL; | | 1016 | dma->map = NULL; |
1017 | } | | 1017 | } |
1018 | } | | 1018 | } |
1019 | | | 1019 | |
1020 | /* fwmem is used to load firmware onto the card */ | | 1020 | /* fwmem is used to load firmware onto the card */ |
1021 | static int | | 1021 | static int |
1022 | iwm_alloc_fwmem(struct iwm_softc *sc) | | 1022 | iwm_alloc_fwmem(struct iwm_softc *sc) |
1023 | { | | 1023 | { |
1024 | /* Must be aligned on a 16-byte boundary. */ | | 1024 | /* Must be aligned on a 16-byte boundary. */ |
1025 | return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, | | 1025 | return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, |
1026 | sc->sc_fwdmasegsz, 16); | | 1026 | sc->sc_fwdmasegsz, 16); |
1027 | } | | 1027 | } |
1028 | | | 1028 | |
1029 | static void | | 1029 | static void |
1030 | iwm_free_fwmem(struct iwm_softc *sc) | | 1030 | iwm_free_fwmem(struct iwm_softc *sc) |
1031 | { | | 1031 | { |
1032 | iwm_dma_contig_free(&sc->fw_dma); | | 1032 | iwm_dma_contig_free(&sc->fw_dma); |
1033 | } | | 1033 | } |
1034 | | | 1034 | |
1035 | /* tx scheduler rings. not used? */ | | 1035 | /* tx scheduler rings. not used? */ |
1036 | static int | | 1036 | static int |
1037 | iwm_alloc_sched(struct iwm_softc *sc) | | 1037 | iwm_alloc_sched(struct iwm_softc *sc) |
1038 | { | | 1038 | { |
1039 | int rv; | | 1039 | int rv; |
1040 | | | 1040 | |
1041 | /* TX scheduler rings must be aligned on a 1KB boundary. */ | | 1041 | /* TX scheduler rings must be aligned on a 1KB boundary. */ |
1042 | rv = iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, | | 1042 | rv = iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, |
1043 | __arraycount(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); | | 1043 | __arraycount(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); |
1044 | return rv; | | 1044 | return rv; |
1045 | } | | 1045 | } |
1046 | | | 1046 | |
1047 | static void | | 1047 | static void |
1048 | iwm_free_sched(struct iwm_softc *sc) | | 1048 | iwm_free_sched(struct iwm_softc *sc) |
1049 | { | | 1049 | { |
1050 | iwm_dma_contig_free(&sc->sched_dma); | | 1050 | iwm_dma_contig_free(&sc->sched_dma); |
1051 | } | | 1051 | } |
1052 | | | 1052 | |
1053 | /* keep-warm page is used internally by the card. see iwl-fh.h for more info */ | | 1053 | /* keep-warm page is used internally by the card. see iwl-fh.h for more info */ |
1054 | static int | | 1054 | static int |
1055 | iwm_alloc_kw(struct iwm_softc *sc) | | 1055 | iwm_alloc_kw(struct iwm_softc *sc) |
1056 | { | | 1056 | { |
1057 | return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); | | 1057 | return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); |
1058 | } | | 1058 | } |
1059 | | | 1059 | |
1060 | static void | | 1060 | static void |
1061 | iwm_free_kw(struct iwm_softc *sc) | | 1061 | iwm_free_kw(struct iwm_softc *sc) |
1062 | { | | 1062 | { |
1063 | iwm_dma_contig_free(&sc->kw_dma); | | 1063 | iwm_dma_contig_free(&sc->kw_dma); |
1064 | } | | 1064 | } |
1065 | | | 1065 | |
1066 | /* interrupt cause table */ | | 1066 | /* interrupt cause table */ |
1067 | static int | | 1067 | static int |
1068 | iwm_alloc_ict(struct iwm_softc *sc) | | 1068 | iwm_alloc_ict(struct iwm_softc *sc) |
1069 | { | | 1069 | { |
1070 | return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, | | 1070 | return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, |
1071 | IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT); | | 1071 | IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT); |
1072 | } | | 1072 | } |
1073 | | | 1073 | |
1074 | static void | | 1074 | static void |
1075 | iwm_free_ict(struct iwm_softc *sc) | | 1075 | iwm_free_ict(struct iwm_softc *sc) |
1076 | { | | 1076 | { |
1077 | iwm_dma_contig_free(&sc->ict_dma); | | 1077 | iwm_dma_contig_free(&sc->ict_dma); |
1078 | } | | 1078 | } |
1079 | | | 1079 | |
1080 | static int | | 1080 | static int |
1081 | iwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) | | 1081 | iwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) |
1082 | { | | 1082 | { |
1083 | bus_size_t size; | | 1083 | bus_size_t size; |
1084 | int i, error; | | 1084 | int i, error; |
1085 | | | 1085 | |
1086 | ring->cur = 0; | | 1086 | ring->cur = 0; |
1087 | | | 1087 | |
1088 | /* Allocate RX descriptors (256-byte aligned). */ | | 1088 | /* Allocate RX descriptors (256-byte aligned). */ |
1089 | size = IWM_RX_RING_COUNT * sizeof(uint32_t); | | 1089 | size = IWM_RX_RING_COUNT * sizeof(uint32_t); |
1090 | error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); | | 1090 | error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); |
1091 | if (error != 0) { | | 1091 | if (error != 0) { |
1092 | aprint_error_dev(sc->sc_dev, | | 1092 | aprint_error_dev(sc->sc_dev, |
1093 | "could not allocate RX ring DMA memory\n"); | | 1093 | "could not allocate RX ring DMA memory\n"); |
1094 | goto fail; | | 1094 | goto fail; |
1095 | } | | 1095 | } |
1096 | ring->desc = ring->desc_dma.vaddr; | | 1096 | ring->desc = ring->desc_dma.vaddr; |
1097 | | | 1097 | |
1098 | /* Allocate RX status area (16-byte aligned). */ | | 1098 | /* Allocate RX status area (16-byte aligned). */ |
1099 | error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, | | 1099 | error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, |
1100 | sizeof(*ring->stat), 16); | | 1100 | sizeof(*ring->stat), 16); |
1101 | if (error != 0) { | | 1101 | if (error != 0) { |
1102 | aprint_error_dev(sc->sc_dev, | | 1102 | aprint_error_dev(sc->sc_dev, |
1103 | "could not allocate RX status DMA memory\n"); | | 1103 | "could not allocate RX status DMA memory\n"); |
1104 | goto fail; | | 1104 | goto fail; |
1105 | } | | 1105 | } |
1106 | ring->stat = ring->stat_dma.vaddr; | | 1106 | ring->stat = ring->stat_dma.vaddr; |
1107 | | | 1107 | |
| @@ -2821,1999 +2821,1999 @@ iwm_load_firmware(struct iwm_softc *sc, | | | @@ -2821,1999 +2821,1999 @@ iwm_load_firmware(struct iwm_softc *sc, |
2821 | } | | 2821 | } |
2822 | | | 2822 | |
2823 | /* iwlwifi: pcie/trans.c */ | | 2823 | /* iwlwifi: pcie/trans.c */ |
2824 | static int | | 2824 | static int |
2825 | iwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) | | 2825 | iwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) |
2826 | { | | 2826 | { |
2827 | int error; | | 2827 | int error; |
2828 | | | 2828 | |
2829 | IWM_WRITE(sc, IWM_CSR_INT, ~0); | | 2829 | IWM_WRITE(sc, IWM_CSR_INT, ~0); |
2830 | | | 2830 | |
2831 | if ((error = iwm_nic_init(sc)) != 0) { | | 2831 | if ((error = iwm_nic_init(sc)) != 0) { |
2832 | aprint_error_dev(sc->sc_dev, "Unable to init nic\n"); | | 2832 | aprint_error_dev(sc->sc_dev, "Unable to init nic\n"); |
2833 | return error; | | 2833 | return error; |
2834 | } | | 2834 | } |
2835 | | | 2835 | |
2836 | /* make sure rfkill handshake bits are cleared */ | | 2836 | /* make sure rfkill handshake bits are cleared */ |
2837 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); | | 2837 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); |
2838 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, | | 2838 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, |
2839 | IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); | | 2839 | IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); |
2840 | | | 2840 | |
2841 | /* clear (again), then enable host interrupts */ | | 2841 | /* clear (again), then enable host interrupts */ |
2842 | IWM_WRITE(sc, IWM_CSR_INT, ~0); | | 2842 | IWM_WRITE(sc, IWM_CSR_INT, ~0); |
2843 | iwm_enable_interrupts(sc); | | 2843 | iwm_enable_interrupts(sc); |
2844 | | | 2844 | |
2845 | /* really make sure rfkill handshake bits are cleared */ | | 2845 | /* really make sure rfkill handshake bits are cleared */ |
2846 | /* maybe we should write a few times more? just to make sure */ | | 2846 | /* maybe we should write a few times more? just to make sure */ |
2847 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); | | 2847 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); |
2848 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); | | 2848 | IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); |
2849 | | | 2849 | |
2850 | /* Load the given image to the HW */ | | 2850 | /* Load the given image to the HW */ |
2851 | error = iwm_load_firmware(sc, ucode_type); | | 2851 | error = iwm_load_firmware(sc, ucode_type); |
2852 | if (error) { | | 2852 | if (error) { |
2853 | aprint_error_dev(sc->sc_dev, "failed to load firmware: %d\n", | | 2853 | aprint_error_dev(sc->sc_dev, "failed to load firmware: %d\n", |
2854 | error); | | 2854 | error); |
2855 | } | | 2855 | } |
2856 | return error; | | 2856 | return error; |
2857 | } | | 2857 | } |
2858 | | | 2858 | |
2859 | static int | | 2859 | static int |
2860 | iwm_fw_alive(struct iwm_softc *sc, uint32_t sched_base) | | 2860 | iwm_fw_alive(struct iwm_softc *sc, uint32_t sched_base) |
2861 | { | | 2861 | { |
2862 | return iwm_post_alive(sc); | | 2862 | return iwm_post_alive(sc); |
2863 | } | | 2863 | } |
2864 | | | 2864 | |
2865 | static int | | 2865 | static int |
2866 | iwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant) | | 2866 | iwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant) |
2867 | { | | 2867 | { |
2868 | struct iwm_tx_ant_cfg_cmd tx_ant_cmd = { | | 2868 | struct iwm_tx_ant_cfg_cmd tx_ant_cmd = { |
2869 | .valid = htole32(valid_tx_ant), | | 2869 | .valid = htole32(valid_tx_ant), |
2870 | }; | | 2870 | }; |
2871 | | | 2871 | |
2872 | return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD, | | 2872 | return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD, |
2873 | IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd); | | 2873 | IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd); |
2874 | } | | 2874 | } |
2875 | | | 2875 | |
2876 | /* iwlwifi: mvm/fw.c */ | | 2876 | /* iwlwifi: mvm/fw.c */ |
2877 | static int | | 2877 | static int |
2878 | iwm_send_phy_cfg_cmd(struct iwm_softc *sc) | | 2878 | iwm_send_phy_cfg_cmd(struct iwm_softc *sc) |
2879 | { | | 2879 | { |
2880 | struct iwm_phy_cfg_cmd phy_cfg_cmd; | | 2880 | struct iwm_phy_cfg_cmd phy_cfg_cmd; |
2881 | enum iwm_ucode_type ucode_type = sc->sc_uc_current; | | 2881 | enum iwm_ucode_type ucode_type = sc->sc_uc_current; |
2882 | | | 2882 | |
2883 | /* Set parameters */ | | 2883 | /* Set parameters */ |
2884 | phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config); | | 2884 | phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config); |
2885 | phy_cfg_cmd.calib_control.event_trigger = | | 2885 | phy_cfg_cmd.calib_control.event_trigger = |
2886 | sc->sc_default_calib[ucode_type].event_trigger; | | 2886 | sc->sc_default_calib[ucode_type].event_trigger; |
2887 | phy_cfg_cmd.calib_control.flow_trigger = | | 2887 | phy_cfg_cmd.calib_control.flow_trigger = |
2888 | sc->sc_default_calib[ucode_type].flow_trigger; | | 2888 | sc->sc_default_calib[ucode_type].flow_trigger; |
2889 | | | 2889 | |
2890 | DPRINTFN(10, ("Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg)); | | 2890 | DPRINTFN(10, ("Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg)); |
2891 | return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC, | | 2891 | return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC, |
2892 | sizeof(phy_cfg_cmd), &phy_cfg_cmd); | | 2892 | sizeof(phy_cfg_cmd), &phy_cfg_cmd); |
2893 | } | | 2893 | } |
2894 | | | 2894 | |
2895 | static int | | 2895 | static int |
2896 | iwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, | | 2896 | iwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, |
2897 | enum iwm_ucode_type ucode_type) | | 2897 | enum iwm_ucode_type ucode_type) |
2898 | { | | 2898 | { |
2899 | enum iwm_ucode_type old_type = sc->sc_uc_current; | | 2899 | enum iwm_ucode_type old_type = sc->sc_uc_current; |
2900 | int error; | | 2900 | int error; |
2901 | | | 2901 | |
2902 | if ((error = iwm_read_firmware(sc)) != 0) | | 2902 | if ((error = iwm_read_firmware(sc)) != 0) |
2903 | return error; | | 2903 | return error; |
2904 | | | 2904 | |
2905 | sc->sc_uc_current = ucode_type; | | 2905 | sc->sc_uc_current = ucode_type; |
2906 | error = iwm_start_fw(sc, ucode_type); | | 2906 | error = iwm_start_fw(sc, ucode_type); |
2907 | if (error) { | | 2907 | if (error) { |
2908 | sc->sc_uc_current = old_type; | | 2908 | sc->sc_uc_current = old_type; |
2909 | return error; | | 2909 | return error; |
2910 | } | | 2910 | } |
2911 | | | 2911 | |
2912 | return iwm_fw_alive(sc, sc->sched_base); | | 2912 | return iwm_fw_alive(sc, sc->sched_base); |
2913 | } | | 2913 | } |
2914 | | | 2914 | |
2915 | /* | | 2915 | /* |
2916 | * mvm misc bits | | 2916 | * mvm misc bits |
2917 | */ | | 2917 | */ |
2918 | | | 2918 | |
2919 | /* | | 2919 | /* |
2920 | * follows iwlwifi/fw.c | | 2920 | * follows iwlwifi/fw.c |
2921 | */ | | 2921 | */ |
2922 | static int | | 2922 | static int |
2923 | iwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) | | 2923 | iwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) |
2924 | { | | 2924 | { |
2925 | int error; | | 2925 | int error; |
2926 | | | 2926 | |
2927 | /* do not operate with rfkill switch turned on */ | | 2927 | /* do not operate with rfkill switch turned on */ |
2928 | if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) { | | 2928 | if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) { |
2929 | aprint_error_dev(sc->sc_dev, | | 2929 | aprint_error_dev(sc->sc_dev, |
2930 | "radio is disabled by hardware switch\n"); | | 2930 | "radio is disabled by hardware switch\n"); |
2931 | return EPERM; | | 2931 | return EPERM; |
2932 | } | | 2932 | } |
2933 | | | 2933 | |
2934 | sc->sc_init_complete = 0; | | 2934 | sc->sc_init_complete = 0; |
2935 | if ((error = iwm_mvm_load_ucode_wait_alive(sc, | | 2935 | if ((error = iwm_mvm_load_ucode_wait_alive(sc, |
2936 | IWM_UCODE_TYPE_INIT)) != 0) | | 2936 | IWM_UCODE_TYPE_INIT)) != 0) |
2937 | return error; | | 2937 | return error; |
2938 | | | 2938 | |
2939 | if (justnvm) { | | 2939 | if (justnvm) { |
2940 | if ((error = iwm_nvm_init(sc)) != 0) { | | 2940 | if ((error = iwm_nvm_init(sc)) != 0) { |
2941 | aprint_error_dev(sc->sc_dev, "failed to read nvm\n"); | | 2941 | aprint_error_dev(sc->sc_dev, "failed to read nvm\n"); |
2942 | return error; | | 2942 | return error; |
2943 | } | | 2943 | } |
2944 | memcpy(&sc->sc_ic.ic_myaddr, | | 2944 | memcpy(&sc->sc_ic.ic_myaddr, |
2945 | &sc->sc_nvm.hw_addr, ETHER_ADDR_LEN); | | 2945 | &sc->sc_nvm.hw_addr, ETHER_ADDR_LEN); |
2946 | | | 2946 | |
2947 | sc->sc_scan_cmd_len = sizeof(struct iwm_scan_cmd) | | 2947 | sc->sc_scan_cmd_len = sizeof(struct iwm_scan_cmd) |
2948 | + sc->sc_capa_max_probe_len | | 2948 | + sc->sc_capa_max_probe_len |
2949 | + IWM_MAX_NUM_SCAN_CHANNELS | | 2949 | + IWM_MAX_NUM_SCAN_CHANNELS |
2950 | * sizeof(struct iwm_scan_channel); | | 2950 | * sizeof(struct iwm_scan_channel); |
2951 | sc->sc_scan_cmd = kmem_alloc(sc->sc_scan_cmd_len, KM_SLEEP); | | 2951 | sc->sc_scan_cmd = kmem_alloc(sc->sc_scan_cmd_len, KM_SLEEP); |
2952 | | | 2952 | |
2953 | return 0; | | 2953 | return 0; |
2954 | } | | 2954 | } |
2955 | | | 2955 | |
2956 | /* Send TX valid antennas before triggering calibrations */ | | 2956 | /* Send TX valid antennas before triggering calibrations */ |
2957 | if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) | | 2957 | if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) |
2958 | return error; | | 2958 | return error; |
2959 | | | 2959 | |
2960 | /* | | 2960 | /* |
2961 | * Send phy configurations command to init uCode | | 2961 | * Send phy configurations command to init uCode |
2962 | * to start the 16.0 uCode init image internal calibrations. | | 2962 | * to start the 16.0 uCode init image internal calibrations. |
2963 | */ | | 2963 | */ |
2964 | if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) { | | 2964 | if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) { |
2965 | DPRINTF(("%s: failed to run internal calibration: %d\n", | | 2965 | DPRINTF(("%s: failed to run internal calibration: %d\n", |
2966 | DEVNAME(sc), error)); | | 2966 | DEVNAME(sc), error)); |
2967 | return error; | | 2967 | return error; |
2968 | } | | 2968 | } |
2969 | | | 2969 | |
2970 | /* | | 2970 | /* |
2971 | * Nothing to do but wait for the init complete notification | | 2971 | * Nothing to do but wait for the init complete notification |
2972 | * from the firmware | | 2972 | * from the firmware |
2973 | */ | | 2973 | */ |
2974 | while (!sc->sc_init_complete) | | 2974 | while (!sc->sc_init_complete) |
2975 | if ((error = tsleep(&sc->sc_init_complete, | | 2975 | if ((error = tsleep(&sc->sc_init_complete, |
2976 | 0, "iwminit", 2*hz)) != 0) | | 2976 | 0, "iwminit", 2*hz)) != 0) |
2977 | break; | | 2977 | break; |
2978 | | | 2978 | |
2979 | return error; | | 2979 | return error; |
2980 | } | | 2980 | } |
2981 | | | 2981 | |
2982 | /* | | 2982 | /* |
2983 | * receive side | | 2983 | * receive side |
2984 | */ | | 2984 | */ |
2985 | | | 2985 | |
2986 | /* (re)stock rx ring, called at init-time and at runtime */ | | 2986 | /* (re)stock rx ring, called at init-time and at runtime */ |
2987 | static int | | 2987 | static int |
2988 | iwm_rx_addbuf(struct iwm_softc *sc, int size, int idx) | | 2988 | iwm_rx_addbuf(struct iwm_softc *sc, int size, int idx) |
2989 | { | | 2989 | { |
2990 | struct iwm_rx_ring *ring = &sc->rxq; | | 2990 | struct iwm_rx_ring *ring = &sc->rxq; |
2991 | struct iwm_rx_data *data = &ring->data[idx]; | | 2991 | struct iwm_rx_data *data = &ring->data[idx]; |
2992 | struct mbuf *m; | | 2992 | struct mbuf *m; |
2993 | int error; | | 2993 | int error; |
2994 | int fatal = 0; | | 2994 | int fatal = 0; |
2995 | | | 2995 | |
2996 | m = m_gethdr(M_DONTWAIT, MT_DATA); | | 2996 | m = m_gethdr(M_DONTWAIT, MT_DATA); |
2997 | if (m == NULL) | | 2997 | if (m == NULL) |
2998 | return ENOBUFS; | | 2998 | return ENOBUFS; |
2999 | | | 2999 | |
3000 | if (size <= MCLBYTES) { | | 3000 | if (size <= MCLBYTES) { |
3001 | MCLGET(m, M_DONTWAIT); | | 3001 | MCLGET(m, M_DONTWAIT); |
3002 | } else { | | 3002 | } else { |
3003 | MEXTMALLOC(m, IWM_RBUF_SIZE, M_DONTWAIT); | | 3003 | MEXTMALLOC(m, IWM_RBUF_SIZE, M_DONTWAIT); |
3004 | } | | 3004 | } |
3005 | if ((m->m_flags & M_EXT) == 0) { | | 3005 | if ((m->m_flags & M_EXT) == 0) { |
3006 | m_freem(m); | | 3006 | m_freem(m); |
3007 | return ENOBUFS; | | 3007 | return ENOBUFS; |
3008 | } | | 3008 | } |
3009 | | | 3009 | |
3010 | if (data->m != NULL) { | | 3010 | if (data->m != NULL) { |
3011 | bus_dmamap_unload(sc->sc_dmat, data->map); | | 3011 | bus_dmamap_unload(sc->sc_dmat, data->map); |
3012 | fatal = 1; | | 3012 | fatal = 1; |
3013 | } | | 3013 | } |
3014 | | | 3014 | |
3015 | m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; | | 3015 | m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; |
3016 | if ((error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, | | 3016 | if ((error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, |
3017 | BUS_DMA_READ|BUS_DMA_NOWAIT)) != 0) { | | 3017 | BUS_DMA_READ|BUS_DMA_NOWAIT)) != 0) { |
3018 | /* XXX */ | | 3018 | /* XXX */ |
3019 | if (fatal) | | 3019 | if (fatal) |
3020 | panic("iwm: could not load RX mbuf"); | | 3020 | panic("iwm: could not load RX mbuf"); |
3021 | m_freem(m); | | 3021 | m_freem(m); |
3022 | return error; | | 3022 | return error; |
3023 | } | | 3023 | } |
3024 | data->m = m; | | 3024 | data->m = m; |
3025 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, size, BUS_DMASYNC_PREREAD); | | 3025 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, size, BUS_DMASYNC_PREREAD); |
3026 | | | 3026 | |
3027 | /* Update RX descriptor. */ | | 3027 | /* Update RX descriptor. */ |
3028 | ring->desc[idx] = htole32(data->map->dm_segs[0].ds_addr >> 8); | | 3028 | ring->desc[idx] = htole32(data->map->dm_segs[0].ds_addr >> 8); |
3029 | bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, | | 3029 | bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, |
3030 | idx * sizeof(uint32_t), sizeof(uint32_t), BUS_DMASYNC_PREWRITE); | | 3030 | idx * sizeof(uint32_t), sizeof(uint32_t), BUS_DMASYNC_PREWRITE); |
3031 | | | 3031 | |
3032 | return 0; | | 3032 | return 0; |
3033 | } | | 3033 | } |
3034 | | | 3034 | |
3035 | /* iwlwifi: mvm/rx.c */ | | 3035 | /* iwlwifi: mvm/rx.c */ |
3036 | #define IWM_RSSI_OFFSET 50 | | 3036 | #define IWM_RSSI_OFFSET 50 |
3037 | static int | | 3037 | static int |
3038 | iwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) | | 3038 | iwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) |
3039 | { | | 3039 | { |
3040 | int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; | | 3040 | int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; |
3041 | uint32_t agc_a, agc_b; | | 3041 | uint32_t agc_a, agc_b; |
3042 | uint32_t val; | | 3042 | uint32_t val; |
3043 | | | 3043 | |
3044 | val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]); | | 3044 | val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]); |
3045 | agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS; | | 3045 | agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS; |
3046 | agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS; | | 3046 | agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS; |
3047 | | | 3047 | |
3048 | val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]); | | 3048 | val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]); |
3049 | rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS; | | 3049 | rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS; |
3050 | rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS; | | 3050 | rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS; |
3051 | | | 3051 | |
3052 | /* | | 3052 | /* |
3053 | * dBm = rssi dB - agc dB - constant. | | 3053 | * dBm = rssi dB - agc dB - constant. |
3054 | * Higher AGC (higher radio gain) means lower signal. | | 3054 | * Higher AGC (higher radio gain) means lower signal. |
3055 | */ | | 3055 | */ |
3056 | rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a; | | 3056 | rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a; |
3057 | rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b; | | 3057 | rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b; |
3058 | max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm); | | 3058 | max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm); |
3059 | | | 3059 | |
3060 | DPRINTF(("Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", | | 3060 | DPRINTF(("Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", |
3061 | rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b)); | | 3061 | rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b)); |
3062 | | | 3062 | |
3063 | return max_rssi_dbm; | | 3063 | return max_rssi_dbm; |
3064 | } | | 3064 | } |
3065 | | | 3065 | |
3066 | /* iwlwifi: mvm/rx.c */ | | 3066 | /* iwlwifi: mvm/rx.c */ |
3067 | /* | | 3067 | /* |
3068 | * iwm_mvm_get_signal_strength - use new rx PHY INFO API | | 3068 | * iwm_mvm_get_signal_strength - use new rx PHY INFO API |
3069 | * values are reported by the fw as positive values - need to negate | | 3069 | * values are reported by the fw as positive values - need to negate |
3070 | * to obtain their dBM. Account for missing antennas by replacing 0 | | 3070 | * to obtain their dBM. Account for missing antennas by replacing 0 |
3071 | * values by -256dBm: practically 0 power and a non-feasible 8 bit value. | | 3071 | * values by -256dBm: practically 0 power and a non-feasible 8 bit value. |
3072 | */ | | 3072 | */ |
3073 | static int | | 3073 | static int |
3074 | iwm_mvm_get_signal_strength(struct iwm_softc *sc, | | 3074 | iwm_mvm_get_signal_strength(struct iwm_softc *sc, |
3075 | struct iwm_rx_phy_info *phy_info) | | 3075 | struct iwm_rx_phy_info *phy_info) |
3076 | { | | 3076 | { |
3077 | int energy_a, energy_b, energy_c, max_energy; | | 3077 | int energy_a, energy_b, energy_c, max_energy; |
3078 | uint32_t val; | | 3078 | uint32_t val; |
3079 | | | 3079 | |
3080 | val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]); | | 3080 | val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]); |
3081 | energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >> | | 3081 | energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >> |
3082 | IWM_RX_INFO_ENERGY_ANT_A_POS; | | 3082 | IWM_RX_INFO_ENERGY_ANT_A_POS; |
3083 | energy_a = energy_a ? -energy_a : -256; | | 3083 | energy_a = energy_a ? -energy_a : -256; |
3084 | energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >> | | 3084 | energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >> |
3085 | IWM_RX_INFO_ENERGY_ANT_B_POS; | | 3085 | IWM_RX_INFO_ENERGY_ANT_B_POS; |
3086 | energy_b = energy_b ? -energy_b : -256; | | 3086 | energy_b = energy_b ? -energy_b : -256; |
3087 | energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >> | | 3087 | energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >> |
3088 | IWM_RX_INFO_ENERGY_ANT_C_POS; | | 3088 | IWM_RX_INFO_ENERGY_ANT_C_POS; |
3089 | energy_c = energy_c ? -energy_c : -256; | | 3089 | energy_c = energy_c ? -energy_c : -256; |
3090 | max_energy = MAX(energy_a, energy_b); | | 3090 | max_energy = MAX(energy_a, energy_b); |
3091 | max_energy = MAX(max_energy, energy_c); | | 3091 | max_energy = MAX(max_energy, energy_c); |
3092 | | | 3092 | |
3093 | DPRINTFN(12, ("energy In A %d B %d C %d, and max %d\n", | | 3093 | DPRINTFN(12, ("energy In A %d B %d C %d, and max %d\n", |
3094 | energy_a, energy_b, energy_c, max_energy)); | | 3094 | energy_a, energy_b, energy_c, max_energy)); |
3095 | | | 3095 | |
3096 | return max_energy; | | 3096 | return max_energy; |
3097 | } | | 3097 | } |
3098 | | | 3098 | |
3099 | static void | | 3099 | static void |
3100 | iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, | | 3100 | iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, |
3101 | struct iwm_rx_packet *pkt, struct iwm_rx_data *data) | | 3101 | struct iwm_rx_packet *pkt, struct iwm_rx_data *data) |
3102 | { | | 3102 | { |
3103 | struct iwm_rx_phy_info *phy_info = (void *)pkt->data; | | 3103 | struct iwm_rx_phy_info *phy_info = (void *)pkt->data; |
3104 | | | 3104 | |
3105 | DPRINTFN(20, ("received PHY stats\n")); | | 3105 | DPRINTFN(20, ("received PHY stats\n")); |
3106 | bus_dmamap_sync(sc->sc_dmat, data->map, sizeof(*pkt), | | 3106 | bus_dmamap_sync(sc->sc_dmat, data->map, sizeof(*pkt), |
3107 | sizeof(*phy_info), BUS_DMASYNC_POSTREAD); | | 3107 | sizeof(*phy_info), BUS_DMASYNC_POSTREAD); |
3108 | | | 3108 | |
3109 | memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info)); | | 3109 | memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info)); |
3110 | } | | 3110 | } |
3111 | | | 3111 | |
3112 | /* | | 3112 | /* |
3113 | * Retrieve the average noise (in dBm) among receivers. | | 3113 | * Retrieve the average noise (in dBm) among receivers. |
3114 | */ | | 3114 | */ |
3115 | static int | | 3115 | static int |
3116 | iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *stats) | | 3116 | iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *stats) |
3117 | { | | 3117 | { |
3118 | int i, total, nbant, noise; | | 3118 | int i, total, nbant, noise; |
3119 | | | 3119 | |
3120 | total = nbant = noise = 0; | | 3120 | total = nbant = noise = 0; |
3121 | for (i = 0; i < 3; i++) { | | 3121 | for (i = 0; i < 3; i++) { |
3122 | noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff; | | 3122 | noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff; |
3123 | if (noise) { | | 3123 | if (noise) { |
3124 | total += noise; | | 3124 | total += noise; |
3125 | nbant++; | | 3125 | nbant++; |
3126 | } | | 3126 | } |
3127 | } | | 3127 | } |
3128 | | | 3128 | |
3129 | /* There should be at least one antenna but check anyway. */ | | 3129 | /* There should be at least one antenna but check anyway. */ |
3130 | return (nbant == 0) ? -127 : (total / nbant) - 107; | | 3130 | return (nbant == 0) ? -127 : (total / nbant) - 107; |
3131 | } | | 3131 | } |
3132 | | | 3132 | |
3133 | /* | | 3133 | /* |
3134 | * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler | | 3134 | * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler |
3135 | * | | 3135 | * |
3136 | * Handles the actual data of the Rx packet from the fw | | 3136 | * Handles the actual data of the Rx packet from the fw |
3137 | */ | | 3137 | */ |
3138 | static void | | 3138 | static void |
3139 | iwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, | | 3139 | iwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, |
3140 | struct iwm_rx_packet *pkt, struct iwm_rx_data *data) | | 3140 | struct iwm_rx_packet *pkt, struct iwm_rx_data *data) |
3141 | { | | 3141 | { |
3142 | struct ieee80211com *ic = &sc->sc_ic; | | 3142 | struct ieee80211com *ic = &sc->sc_ic; |
3143 | struct ieee80211_frame *wh; | | 3143 | struct ieee80211_frame *wh; |
3144 | struct ieee80211_node *ni; | | 3144 | struct ieee80211_node *ni; |
3145 | struct ieee80211_channel *c = NULL; | | 3145 | struct ieee80211_channel *c = NULL; |
3146 | struct mbuf *m; | | 3146 | struct mbuf *m; |
3147 | struct iwm_rx_phy_info *phy_info; | | 3147 | struct iwm_rx_phy_info *phy_info; |
3148 | struct iwm_rx_mpdu_res_start *rx_res; | | 3148 | struct iwm_rx_mpdu_res_start *rx_res; |
3149 | int device_timestamp; | | 3149 | int device_timestamp; |
3150 | uint32_t len; | | 3150 | uint32_t len; |
3151 | uint32_t rx_pkt_status; | | 3151 | uint32_t rx_pkt_status; |
3152 | int rssi; | | 3152 | int rssi; |
3153 | | | 3153 | |
3154 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, | | 3154 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, |
3155 | BUS_DMASYNC_POSTREAD); | | 3155 | BUS_DMASYNC_POSTREAD); |
3156 | | | 3156 | |
3157 | phy_info = &sc->sc_last_phy_info; | | 3157 | phy_info = &sc->sc_last_phy_info; |
3158 | rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; | | 3158 | rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; |
3159 | wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); | | 3159 | wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); |
3160 | len = le16toh(rx_res->byte_count); | | 3160 | len = le16toh(rx_res->byte_count); |
3161 | rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len)); | | 3161 | rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len)); |
3162 | | | 3162 | |
3163 | m = data->m; | | 3163 | m = data->m; |
3164 | m->m_data = pkt->data + sizeof(*rx_res); | | 3164 | m->m_data = pkt->data + sizeof(*rx_res); |
3165 | m->m_pkthdr.len = m->m_len = len; | | 3165 | m->m_pkthdr.len = m->m_len = len; |
3166 | | | 3166 | |
3167 | if (__predict_false(phy_info->cfg_phy_cnt > 20)) { | | 3167 | if (__predict_false(phy_info->cfg_phy_cnt > 20)) { |
3168 | DPRINTF(("dsp size out of range [0,20]: %d\n", | | 3168 | DPRINTF(("dsp size out of range [0,20]: %d\n", |
3169 | phy_info->cfg_phy_cnt)); | | 3169 | phy_info->cfg_phy_cnt)); |
3170 | return; | | 3170 | return; |
3171 | } | | 3171 | } |
3172 | | | 3172 | |
3173 | if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || | | 3173 | if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || |
3174 | !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) { | | 3174 | !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) { |
3175 | DPRINTF(("Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status)); | | 3175 | DPRINTF(("Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status)); |
3176 | return; /* drop */ | | 3176 | return; /* drop */ |
3177 | } | | 3177 | } |
3178 | | | 3178 | |
3179 | device_timestamp = le32toh(phy_info->system_timestamp); | | 3179 | device_timestamp = le32toh(phy_info->system_timestamp); |
3180 | | | 3180 | |
3181 | if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) { | | 3181 | if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) { |
3182 | rssi = iwm_mvm_get_signal_strength(sc, phy_info); | | 3182 | rssi = iwm_mvm_get_signal_strength(sc, phy_info); |
3183 | } else { | | 3183 | } else { |
3184 | rssi = iwm_mvm_calc_rssi(sc, phy_info); | | 3184 | rssi = iwm_mvm_calc_rssi(sc, phy_info); |
3185 | } | | 3185 | } |
3186 | rssi = -rssi; | | 3186 | rssi = -rssi; |
3187 | | | 3187 | |
3188 | if (ic->ic_state == IEEE80211_S_SCAN) | | 3188 | if (ic->ic_state == IEEE80211_S_SCAN) |
3189 | iwm_fix_channel(ic, m); | | 3189 | iwm_fix_channel(ic, m); |
3190 | | | 3190 | |
3191 | /* replenish ring for the buffer we're going to feed to the sharks */ | | 3191 | /* replenish ring for the buffer we're going to feed to the sharks */ |
3192 | if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) | | 3192 | if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) |
3193 | return; | | 3193 | return; |
3194 | | | 3194 | |
3195 | m->m_pkthdr.rcvif = IC2IFP(ic); | | 3195 | m->m_pkthdr.rcvif = IC2IFP(ic); |
3196 | | | 3196 | |
3197 | if (sc->sc_scanband == IEEE80211_CHAN_5GHZ) { | | 3197 | if (sc->sc_scanband == IEEE80211_CHAN_5GHZ) { |
3198 | if (le32toh(phy_info->channel) < __arraycount(ic->ic_channels)) | | 3198 | if (le32toh(phy_info->channel) < __arraycount(ic->ic_channels)) |
3199 | c = &ic->ic_channels[le32toh(phy_info->channel)]; | | 3199 | c = &ic->ic_channels[le32toh(phy_info->channel)]; |
3200 | } | | 3200 | } |
3201 | | | 3201 | |
3202 | ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); | | 3202 | ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); |
3203 | if (c) | | 3203 | if (c) |
3204 | ni->ni_chan = c; | | 3204 | ni->ni_chan = c; |
3205 | | | 3205 | |
3206 | if (sc->sc_drvbpf != NULL) { | | 3206 | if (sc->sc_drvbpf != NULL) { |
3207 | struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap; | | 3207 | struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap; |
3208 | | | 3208 | |
3209 | tap->wr_flags = 0; | | 3209 | tap->wr_flags = 0; |
3210 | if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE)) | | 3210 | if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE)) |
3211 | tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; | | 3211 | tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; |
3212 | tap->wr_chan_freq = | | 3212 | tap->wr_chan_freq = |
3213 | htole16(ic->ic_channels[phy_info->channel].ic_freq); | | 3213 | htole16(ic->ic_channels[phy_info->channel].ic_freq); |
3214 | tap->wr_chan_flags = | | 3214 | tap->wr_chan_flags = |
3215 | htole16(ic->ic_channels[phy_info->channel].ic_flags); | | 3215 | htole16(ic->ic_channels[phy_info->channel].ic_flags); |
3216 | tap->wr_dbm_antsignal = (int8_t)rssi; | | 3216 | tap->wr_dbm_antsignal = (int8_t)rssi; |
3217 | tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; | | 3217 | tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; |
3218 | tap->wr_tsft = phy_info->system_timestamp; | | 3218 | tap->wr_tsft = phy_info->system_timestamp; |
3219 | switch (phy_info->rate) { | | 3219 | switch (phy_info->rate) { |
3220 | /* CCK rates. */ | | 3220 | /* CCK rates. */ |
3221 | case 10: tap->wr_rate = 2; break; | | 3221 | case 10: tap->wr_rate = 2; break; |
3222 | case 20: tap->wr_rate = 4; break; | | 3222 | case 20: tap->wr_rate = 4; break; |
3223 | case 55: tap->wr_rate = 11; break; | | 3223 | case 55: tap->wr_rate = 11; break; |
3224 | case 110: tap->wr_rate = 22; break; | | 3224 | case 110: tap->wr_rate = 22; break; |
3225 | /* OFDM rates. */ | | 3225 | /* OFDM rates. */ |
3226 | case 0xd: tap->wr_rate = 12; break; | | 3226 | case 0xd: tap->wr_rate = 12; break; |
3227 | case 0xf: tap->wr_rate = 18; break; | | 3227 | case 0xf: tap->wr_rate = 18; break; |
3228 | case 0x5: tap->wr_rate = 24; break; | | 3228 | case 0x5: tap->wr_rate = 24; break; |
3229 | case 0x7: tap->wr_rate = 36; break; | | 3229 | case 0x7: tap->wr_rate = 36; break; |
3230 | case 0x9: tap->wr_rate = 48; break; | | 3230 | case 0x9: tap->wr_rate = 48; break; |
3231 | case 0xb: tap->wr_rate = 72; break; | | 3231 | case 0xb: tap->wr_rate = 72; break; |
3232 | case 0x1: tap->wr_rate = 96; break; | | 3232 | case 0x1: tap->wr_rate = 96; break; |
3233 | case 0x3: tap->wr_rate = 108; break; | | 3233 | case 0x3: tap->wr_rate = 108; break; |
3234 | /* Unknown rate: should not happen. */ | | 3234 | /* Unknown rate: should not happen. */ |
3235 | default: tap->wr_rate = 0; | | 3235 | default: tap->wr_rate = 0; |
3236 | } | | 3236 | } |
3237 | | | 3237 | |
3238 | bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); | | 3238 | bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); |
3239 | } | | 3239 | } |
3240 | ieee80211_input(ic, m, ni, rssi, device_timestamp); | | 3240 | ieee80211_input(ic, m, ni, rssi, device_timestamp); |
3241 | ieee80211_free_node(ni); | | 3241 | ieee80211_free_node(ni); |
3242 | } | | 3242 | } |
3243 | | | 3243 | |
3244 | static void | | 3244 | static void |
3245 | iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, | | 3245 | iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, |
3246 | struct iwm_node *in) | | 3246 | struct iwm_node *in) |
3247 | { | | 3247 | { |
3248 | struct ieee80211com *ic = &sc->sc_ic; | | 3248 | struct ieee80211com *ic = &sc->sc_ic; |
3249 | struct ifnet *ifp = IC2IFP(ic); | | 3249 | struct ifnet *ifp = IC2IFP(ic); |
3250 | struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; | | 3250 | struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; |
3251 | int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; | | 3251 | int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; |
3252 | int failack = tx_resp->failure_frame; | | 3252 | int failack = tx_resp->failure_frame; |
3253 | | | 3253 | |
3254 | KASSERT(tx_resp->frame_count == 1); | | 3254 | KASSERT(tx_resp->frame_count == 1); |
3255 | | | 3255 | |
3256 | /* Update rate control statistics. */ | | 3256 | /* Update rate control statistics. */ |
3257 | in->in_amn.amn_txcnt++; | | 3257 | in->in_amn.amn_txcnt++; |
3258 | if (failack > 0) { | | 3258 | if (failack > 0) { |
3259 | in->in_amn.amn_retrycnt++; | | 3259 | in->in_amn.amn_retrycnt++; |
3260 | } | | 3260 | } |
3261 | | | 3261 | |
3262 | if (status != IWM_TX_STATUS_SUCCESS && | | 3262 | if (status != IWM_TX_STATUS_SUCCESS && |
3263 | status != IWM_TX_STATUS_DIRECT_DONE) | | 3263 | status != IWM_TX_STATUS_DIRECT_DONE) |
3264 | ifp->if_oerrors++; | | 3264 | ifp->if_oerrors++; |
3265 | else | | 3265 | else |
3266 | ifp->if_opackets++; | | 3266 | ifp->if_opackets++; |
3267 | } | | 3267 | } |
3268 | | | 3268 | |
3269 | static void | | 3269 | static void |
3270 | iwm_mvm_rx_tx_cmd(struct iwm_softc *sc, | | 3270 | iwm_mvm_rx_tx_cmd(struct iwm_softc *sc, |
3271 | struct iwm_rx_packet *pkt, struct iwm_rx_data *data) | | 3271 | struct iwm_rx_packet *pkt, struct iwm_rx_data *data) |
3272 | { | | 3272 | { |
3273 | struct ieee80211com *ic = &sc->sc_ic; | | 3273 | struct ieee80211com *ic = &sc->sc_ic; |
3274 | struct ifnet *ifp = IC2IFP(ic); | | 3274 | struct ifnet *ifp = IC2IFP(ic); |
3275 | struct iwm_cmd_header *cmd_hdr = &pkt->hdr; | | 3275 | struct iwm_cmd_header *cmd_hdr = &pkt->hdr; |
3276 | int idx = cmd_hdr->idx; | | 3276 | int idx = cmd_hdr->idx; |
3277 | int qid = cmd_hdr->qid; | | 3277 | int qid = cmd_hdr->qid; |
3278 | struct iwm_tx_ring *ring = &sc->txq[qid]; | | 3278 | struct iwm_tx_ring *ring = &sc->txq[qid]; |
3279 | struct iwm_tx_data *txd = &ring->data[idx]; | | 3279 | struct iwm_tx_data *txd = &ring->data[idx]; |
3280 | struct iwm_node *in = txd->in; | | 3280 | struct iwm_node *in = txd->in; |
3281 | | | 3281 | |
3282 | if (txd->done) { | | 3282 | if (txd->done) { |
3283 | DPRINTF(("%s: got tx interrupt that's already been handled!\n", | | 3283 | DPRINTF(("%s: got tx interrupt that's already been handled!\n", |
3284 | DEVNAME(sc))); | | 3284 | DEVNAME(sc))); |
3285 | return; | | 3285 | return; |
3286 | } | | 3286 | } |
3287 | | | 3287 | |
3288 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, | | 3288 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, IWM_RBUF_SIZE, |
3289 | BUS_DMASYNC_POSTREAD); | | 3289 | BUS_DMASYNC_POSTREAD); |
3290 | | | 3290 | |
3291 | sc->sc_tx_timer = 0; | | 3291 | sc->sc_tx_timer = 0; |
3292 | | | 3292 | |
3293 | iwm_mvm_rx_tx_cmd_single(sc, pkt, in); | | 3293 | iwm_mvm_rx_tx_cmd_single(sc, pkt, in); |
3294 | | | 3294 | |
3295 | /* Unmap and free mbuf. */ | | 3295 | /* Unmap and free mbuf. */ |
3296 | bus_dmamap_sync(sc->sc_dmat, txd->map, 0, txd->map->dm_mapsize, | | 3296 | bus_dmamap_sync(sc->sc_dmat, txd->map, 0, txd->map->dm_mapsize, |
3297 | BUS_DMASYNC_POSTWRITE); | | 3297 | BUS_DMASYNC_POSTWRITE); |
3298 | bus_dmamap_unload(sc->sc_dmat, txd->map); | | 3298 | bus_dmamap_unload(sc->sc_dmat, txd->map); |
3299 | m_freem(txd->m); | | 3299 | m_freem(txd->m); |
3300 | | | 3300 | |
3301 | DPRINTFN(8, ("free txd %p, in %p\n", txd, txd->in)); | | 3301 | DPRINTFN(8, ("free txd %p, in %p\n", txd, txd->in)); |
3302 | KASSERT(txd->done == 0); | | 3302 | KASSERT(txd->done == 0); |
3303 | txd->done = 1; | | 3303 | txd->done = 1; |
3304 | KASSERT(txd->in); | | 3304 | KASSERT(txd->in); |
3305 | | | 3305 | |
3306 | txd->m = NULL; | | 3306 | txd->m = NULL; |
3307 | txd->in = NULL; | | 3307 | txd->in = NULL; |
3308 | ieee80211_free_node(&in->in_ni); | | 3308 | ieee80211_free_node(&in->in_ni); |
3309 | | | 3309 | |
3310 | if (--ring->queued < IWM_TX_RING_LOMARK) { | | 3310 | if (--ring->queued < IWM_TX_RING_LOMARK) { |
3311 | sc->qfullmsk &= ~(1 << ring->qid); | | 3311 | sc->qfullmsk &= ~(1 << ring->qid); |
3312 | if (sc->qfullmsk == 0 && (ifp->if_flags & IFF_OACTIVE)) { | | 3312 | if (sc->qfullmsk == 0 && (ifp->if_flags & IFF_OACTIVE)) { |
3313 | ifp->if_flags &= ~IFF_OACTIVE; | | 3313 | ifp->if_flags &= ~IFF_OACTIVE; |
3314 | /* | | 3314 | /* |
3315 | * Well, we're in interrupt context, but then again | | 3315 | * Well, we're in interrupt context, but then again |
3316 | * I guess net80211 does all sorts of stunts in | | 3316 | * I guess net80211 does all sorts of stunts in |
3317 | * interrupt context, so maybe this is no biggie. | | 3317 | * interrupt context, so maybe this is no biggie. |
3318 | */ | | 3318 | */ |
3319 | (*ifp->if_start)(ifp); | | 3319 | (*ifp->if_start)(ifp); |
3320 | } | | 3320 | } |
3321 | } | | 3321 | } |
3322 | } | | 3322 | } |
3323 | | | 3323 | |
3324 | /* | | 3324 | /* |
3325 | * BEGIN iwlwifi/mvm/binding.c | | 3325 | * BEGIN iwlwifi/mvm/binding.c |
3326 | */ | | 3326 | */ |
3327 | | | 3327 | |
3328 | static int | | 3328 | static int |
3329 | iwm_mvm_binding_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action) | | 3329 | iwm_mvm_binding_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action) |
3330 | { | | 3330 | { |
3331 | struct iwm_binding_cmd cmd; | | 3331 | struct iwm_binding_cmd cmd; |
3332 | struct iwm_mvm_phy_ctxt *phyctxt = in->in_phyctxt; | | 3332 | struct iwm_mvm_phy_ctxt *phyctxt = in->in_phyctxt; |
3333 | int i, ret; | | 3333 | int i, ret; |
3334 | uint32_t status; | | 3334 | uint32_t status; |
3335 | | | 3335 | |
3336 | memset(&cmd, 0, sizeof(cmd)); | | 3336 | memset(&cmd, 0, sizeof(cmd)); |
3337 | | | 3337 | |
3338 | cmd.id_and_color | | 3338 | cmd.id_and_color |
3339 | = htole32(IWM_FW_CMD_ID_AND_COLOR(phyctxt->id, phyctxt->color)); | | 3339 | = htole32(IWM_FW_CMD_ID_AND_COLOR(phyctxt->id, phyctxt->color)); |
3340 | cmd.action = htole32(action); | | 3340 | cmd.action = htole32(action); |
3341 | cmd.phy = htole32(IWM_FW_CMD_ID_AND_COLOR(phyctxt->id, phyctxt->color)); | | 3341 | cmd.phy = htole32(IWM_FW_CMD_ID_AND_COLOR(phyctxt->id, phyctxt->color)); |
3342 | | | 3342 | |
3343 | cmd.macs[0] = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color)); | | 3343 | cmd.macs[0] = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color)); |
3344 | for (i = 1; i < IWM_MAX_MACS_IN_BINDING; i++) | | 3344 | for (i = 1; i < IWM_MAX_MACS_IN_BINDING; i++) |
3345 | cmd.macs[i] = htole32(IWM_FW_CTXT_INVALID); | | 3345 | cmd.macs[i] = htole32(IWM_FW_CTXT_INVALID); |
3346 | | | 3346 | |
3347 | status = 0; | | 3347 | status = 0; |
3348 | ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_BINDING_CONTEXT_CMD, | | 3348 | ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_BINDING_CONTEXT_CMD, |
3349 | sizeof(cmd), &cmd, &status); | | 3349 | sizeof(cmd), &cmd, &status); |
3350 | if (ret) { | | 3350 | if (ret) { |
3351 | DPRINTF(("%s: Failed to send binding (action:%d): %d\n", | | 3351 | DPRINTF(("%s: Failed to send binding (action:%d): %d\n", |
3352 | DEVNAME(sc), action, ret)); | | 3352 | DEVNAME(sc), action, ret)); |
3353 | return ret; | | 3353 | return ret; |
3354 | } | | 3354 | } |
3355 | | | 3355 | |
3356 | if (status) { | | 3356 | if (status) { |
3357 | DPRINTF(("%s: Binding command failed: %u\n", DEVNAME(sc), | | 3357 | DPRINTF(("%s: Binding command failed: %u\n", DEVNAME(sc), |
3358 | status)); | | 3358 | status)); |
3359 | ret = EIO; | | 3359 | ret = EIO; |
3360 | } | | 3360 | } |
3361 | | | 3361 | |
3362 | return ret; | | 3362 | return ret; |
3363 | } | | 3363 | } |
3364 | | | 3364 | |
3365 | static int | | 3365 | static int |
3366 | iwm_mvm_binding_update(struct iwm_softc *sc, struct iwm_node *in, int add) | | 3366 | iwm_mvm_binding_update(struct iwm_softc *sc, struct iwm_node *in, int add) |
3367 | { | | 3367 | { |
3368 | return iwm_mvm_binding_cmd(sc, in, IWM_FW_CTXT_ACTION_ADD); | | 3368 | return iwm_mvm_binding_cmd(sc, in, IWM_FW_CTXT_ACTION_ADD); |
3369 | } | | 3369 | } |
3370 | | | 3370 | |
3371 | static int | | 3371 | static int |
3372 | iwm_mvm_binding_add_vif(struct iwm_softc *sc, struct iwm_node *in) | | 3372 | iwm_mvm_binding_add_vif(struct iwm_softc *sc, struct iwm_node *in) |
3373 | { | | 3373 | { |
3374 | return iwm_mvm_binding_update(sc, in, IWM_FW_CTXT_ACTION_ADD); | | 3374 | return iwm_mvm_binding_update(sc, in, IWM_FW_CTXT_ACTION_ADD); |
3375 | } | | 3375 | } |
3376 | | | 3376 | |
3377 | /* | | 3377 | /* |
3378 | * END iwlwifi/mvm/binding.c | | 3378 | * END iwlwifi/mvm/binding.c |
3379 | */ | | 3379 | */ |
3380 | | | 3380 | |
3381 | /* | | 3381 | /* |
3382 | * BEGIN iwlwifi/mvm/phy-ctxt.c | | 3382 | * BEGIN iwlwifi/mvm/phy-ctxt.c |
3383 | */ | | 3383 | */ |
3384 | | | 3384 | |
3385 | /* | | 3385 | /* |
3386 | * Construct the generic fields of the PHY context command | | 3386 | * Construct the generic fields of the PHY context command |
3387 | */ | | 3387 | */ |
3388 | static void | | 3388 | static void |
3389 | iwm_mvm_phy_ctxt_cmd_hdr(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, | | 3389 | iwm_mvm_phy_ctxt_cmd_hdr(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, |
3390 | struct iwm_phy_context_cmd *cmd, uint32_t action, uint32_t apply_time) | | 3390 | struct iwm_phy_context_cmd *cmd, uint32_t action, uint32_t apply_time) |
3391 | { | | 3391 | { |
3392 | memset(cmd, 0, sizeof(struct iwm_phy_context_cmd)); | | 3392 | memset(cmd, 0, sizeof(struct iwm_phy_context_cmd)); |
3393 | | | 3393 | |
3394 | cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ctxt->id, | | 3394 | cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ctxt->id, |
3395 | ctxt->color)); | | 3395 | ctxt->color)); |
3396 | cmd->action = htole32(action); | | 3396 | cmd->action = htole32(action); |
3397 | cmd->apply_time = htole32(apply_time); | | 3397 | cmd->apply_time = htole32(apply_time); |
3398 | } | | 3398 | } |
3399 | | | 3399 | |
3400 | /* | | 3400 | /* |
3401 | * Add the phy configuration to the PHY context command | | 3401 | * Add the phy configuration to the PHY context command |
3402 | */ | | 3402 | */ |
3403 | static void | | 3403 | static void |
3404 | iwm_mvm_phy_ctxt_cmd_data(struct iwm_softc *sc, | | 3404 | iwm_mvm_phy_ctxt_cmd_data(struct iwm_softc *sc, |
3405 | struct iwm_phy_context_cmd *cmd, struct ieee80211_channel *chan, | | 3405 | struct iwm_phy_context_cmd *cmd, struct ieee80211_channel *chan, |
3406 | uint8_t chains_static, uint8_t chains_dynamic) | | 3406 | uint8_t chains_static, uint8_t chains_dynamic) |
3407 | { | | 3407 | { |
3408 | struct ieee80211com *ic = &sc->sc_ic; | | 3408 | struct ieee80211com *ic = &sc->sc_ic; |
3409 | uint8_t active_cnt, idle_cnt; | | 3409 | uint8_t active_cnt, idle_cnt; |
3410 | | | 3410 | |
3411 | cmd->ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ? | | 3411 | cmd->ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ? |
3412 | IWM_PHY_BAND_24 : IWM_PHY_BAND_5; | | 3412 | IWM_PHY_BAND_24 : IWM_PHY_BAND_5; |
3413 | | | 3413 | |
3414 | cmd->ci.channel = ieee80211_chan2ieee(ic, chan); | | 3414 | cmd->ci.channel = ieee80211_chan2ieee(ic, chan); |
3415 | cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE20; | | 3415 | cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE20; |
3416 | cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW; | | 3416 | cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW; |
3417 | | | 3417 | |
3418 | /* Set rx the chains */ | | 3418 | /* Set rx the chains */ |
3419 | idle_cnt = chains_static; | | 3419 | idle_cnt = chains_static; |
3420 | active_cnt = chains_dynamic; | | 3420 | active_cnt = chains_dynamic; |
3421 | | | 3421 | |
3422 | cmd->rxchain_info = htole32(IWM_FW_VALID_RX_ANT(sc) << | | 3422 | cmd->rxchain_info = htole32(IWM_FW_VALID_RX_ANT(sc) << |
3423 | IWM_PHY_RX_CHAIN_VALID_POS); | | 3423 | IWM_PHY_RX_CHAIN_VALID_POS); |
3424 | cmd->rxchain_info |= htole32(idle_cnt << IWM_PHY_RX_CHAIN_CNT_POS); | | 3424 | cmd->rxchain_info |= htole32(idle_cnt << IWM_PHY_RX_CHAIN_CNT_POS); |
3425 | cmd->rxchain_info |= htole32(active_cnt << | | 3425 | cmd->rxchain_info |= htole32(active_cnt << |
3426 | IWM_PHY_RX_CHAIN_MIMO_CNT_POS); | | 3426 | IWM_PHY_RX_CHAIN_MIMO_CNT_POS); |
3427 | | | 3427 | |
3428 | cmd->txchain_info = htole32(IWM_FW_VALID_TX_ANT(sc)); | | 3428 | cmd->txchain_info = htole32(IWM_FW_VALID_TX_ANT(sc)); |
3429 | } | | 3429 | } |
3430 | | | 3430 | |
3431 | /* | | 3431 | /* |
3432 | * Send a command | | 3432 | * Send a command |
3433 | * only if something in the configuration changed: in case that this is the | | 3433 | * only if something in the configuration changed: in case that this is the |
3434 | * first time that the phy configuration is applied or in case that the phy | | 3434 | * first time that the phy configuration is applied or in case that the phy |
3435 | * configuration changed from the previous apply. | | 3435 | * configuration changed from the previous apply. |
3436 | */ | | 3436 | */ |
3437 | static int | | 3437 | static int |
3438 | iwm_mvm_phy_ctxt_apply(struct iwm_softc *sc, | | 3438 | iwm_mvm_phy_ctxt_apply(struct iwm_softc *sc, |
3439 | struct iwm_mvm_phy_ctxt *ctxt, | | 3439 | struct iwm_mvm_phy_ctxt *ctxt, |
3440 | uint8_t chains_static, uint8_t chains_dynamic, | | 3440 | uint8_t chains_static, uint8_t chains_dynamic, |
3441 | uint32_t action, uint32_t apply_time) | | 3441 | uint32_t action, uint32_t apply_time) |
3442 | { | | 3442 | { |
3443 | struct iwm_phy_context_cmd cmd; | | 3443 | struct iwm_phy_context_cmd cmd; |
3444 | int ret; | | 3444 | int ret; |
3445 | | | 3445 | |
3446 | /* Set the command header fields */ | | 3446 | /* Set the command header fields */ |
3447 | iwm_mvm_phy_ctxt_cmd_hdr(sc, ctxt, &cmd, action, apply_time); | | 3447 | iwm_mvm_phy_ctxt_cmd_hdr(sc, ctxt, &cmd, action, apply_time); |
3448 | | | 3448 | |
3449 | /* Set the command data */ | | 3449 | /* Set the command data */ |
3450 | iwm_mvm_phy_ctxt_cmd_data(sc, &cmd, ctxt->channel, | | 3450 | iwm_mvm_phy_ctxt_cmd_data(sc, &cmd, ctxt->channel, |
3451 | chains_static, chains_dynamic); | | 3451 | chains_static, chains_dynamic); |
3452 | | | 3452 | |
3453 | ret = iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONTEXT_CMD, IWM_CMD_SYNC, | | 3453 | ret = iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONTEXT_CMD, IWM_CMD_SYNC, |
3454 | sizeof(struct iwm_phy_context_cmd), &cmd); | | 3454 | sizeof(struct iwm_phy_context_cmd), &cmd); |
3455 | if (ret) { | | 3455 | if (ret) { |
3456 | DPRINTF(("PHY ctxt cmd error. ret=%d\n", ret)); | | 3456 | DPRINTF(("PHY ctxt cmd error. ret=%d\n", ret)); |
3457 | } | | 3457 | } |
3458 | return ret; | | 3458 | return ret; |
3459 | } | | 3459 | } |
3460 | | | 3460 | |
3461 | /* | | 3461 | /* |
3462 | * Send a command to add a PHY context based on the current HW configuration. | | 3462 | * Send a command to add a PHY context based on the current HW configuration. |
3463 | */ | | 3463 | */ |
3464 | static int | | 3464 | static int |
3465 | iwm_mvm_phy_ctxt_add(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, | | 3465 | iwm_mvm_phy_ctxt_add(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, |
3466 | struct ieee80211_channel *chan, | | 3466 | struct ieee80211_channel *chan, |
3467 | uint8_t chains_static, uint8_t chains_dynamic) | | 3467 | uint8_t chains_static, uint8_t chains_dynamic) |
3468 | { | | 3468 | { |
3469 | ctxt->channel = chan; | | 3469 | ctxt->channel = chan; |
3470 | return iwm_mvm_phy_ctxt_apply(sc, ctxt, | | 3470 | return iwm_mvm_phy_ctxt_apply(sc, ctxt, |
3471 | chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_ADD, 0); | | 3471 | chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_ADD, 0); |
3472 | } | | 3472 | } |
3473 | | | 3473 | |
3474 | /* | | 3474 | /* |
3475 | * Send a command to modify the PHY context based on the current HW | | 3475 | * Send a command to modify the PHY context based on the current HW |
3476 | * configuration. Note that the function does not check that the configuration | | 3476 | * configuration. Note that the function does not check that the configuration |
3477 | * changed. | | 3477 | * changed. |
3478 | */ | | 3478 | */ |
3479 | static int | | 3479 | static int |
3480 | iwm_mvm_phy_ctxt_changed(struct iwm_softc *sc, | | 3480 | iwm_mvm_phy_ctxt_changed(struct iwm_softc *sc, |
3481 | struct iwm_mvm_phy_ctxt *ctxt, struct ieee80211_channel *chan, | | 3481 | struct iwm_mvm_phy_ctxt *ctxt, struct ieee80211_channel *chan, |
3482 | uint8_t chains_static, uint8_t chains_dynamic) | | 3482 | uint8_t chains_static, uint8_t chains_dynamic) |
3483 | { | | 3483 | { |
3484 | ctxt->channel = chan; | | 3484 | ctxt->channel = chan; |
3485 | return iwm_mvm_phy_ctxt_apply(sc, ctxt, | | 3485 | return iwm_mvm_phy_ctxt_apply(sc, ctxt, |
3486 | chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_MODIFY, 0); | | 3486 | chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_MODIFY, 0); |
3487 | } | | 3487 | } |
3488 | | | 3488 | |
3489 | /* | | 3489 | /* |
3490 | * END iwlwifi/mvm/phy-ctxt.c | | 3490 | * END iwlwifi/mvm/phy-ctxt.c |
3491 | */ | | 3491 | */ |
3492 | | | 3492 | |
3493 | /* | | 3493 | /* |
3494 | * transmit side | | 3494 | * transmit side |
3495 | */ | | 3495 | */ |
3496 | | | 3496 | |
3497 | /* | | 3497 | /* |
3498 | * Send a command to the firmware. We try to implement the Linux | | 3498 | * Send a command to the firmware. We try to implement the Linux |
3499 | * driver interface for the routine. | | 3499 | * driver interface for the routine. |
3500 | * mostly from if_iwn (iwn_cmd()). | | 3500 | * mostly from if_iwn (iwn_cmd()). |
3501 | * | | 3501 | * |
3502 | * For now, we always copy the first part and map the second one (if it exists). | | 3502 | * For now, we always copy the first part and map the second one (if it exists). |
3503 | */ | | 3503 | */ |
3504 | static int | | 3504 | static int |
3505 | iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) | | 3505 | iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) |
3506 | { | | 3506 | { |
3507 | struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; | | 3507 | struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; |
3508 | struct iwm_tfd *desc; | | 3508 | struct iwm_tfd *desc; |
3509 | struct iwm_tx_data *data; | | 3509 | struct iwm_tx_data *data; |
3510 | struct iwm_device_cmd *cmd; | | 3510 | struct iwm_device_cmd *cmd; |
3511 | struct mbuf *m; | | 3511 | struct mbuf *m; |
3512 | bus_addr_t paddr; | | 3512 | bus_addr_t paddr; |
3513 | uint32_t addr_lo; | | 3513 | uint32_t addr_lo; |
3514 | int error = 0, i, paylen, off, s; | | 3514 | int error = 0, i, paylen, off, s; |
3515 | int code; | | 3515 | int code; |
3516 | int async, wantresp; | | 3516 | int async, wantresp; |
3517 | | | 3517 | |
3518 | code = hcmd->id; | | 3518 | code = hcmd->id; |
3519 | async = hcmd->flags & IWM_CMD_ASYNC; | | 3519 | async = hcmd->flags & IWM_CMD_ASYNC; |
3520 | wantresp = hcmd->flags & IWM_CMD_WANT_SKB; | | 3520 | wantresp = hcmd->flags & IWM_CMD_WANT_SKB; |
3521 | | | 3521 | |
3522 | for (i = 0, paylen = 0; i < __arraycount(hcmd->len); i++) { | | 3522 | for (i = 0, paylen = 0; i < __arraycount(hcmd->len); i++) { |
3523 | paylen += hcmd->len[i]; | | 3523 | paylen += hcmd->len[i]; |
3524 | } | | 3524 | } |
3525 | | | 3525 | |
3526 | /* if the command wants an answer, busy sc_cmd_resp */ | | 3526 | /* if the command wants an answer, busy sc_cmd_resp */ |
3527 | if (wantresp) { | | 3527 | if (wantresp) { |
3528 | KASSERT(!async); | | 3528 | KASSERT(!async); |
3529 | while (sc->sc_wantresp != -1) | | 3529 | while (sc->sc_wantresp != -1) |
3530 | tsleep(&sc->sc_wantresp, 0, "iwmcmdsl", 0); | | 3530 | tsleep(&sc->sc_wantresp, 0, "iwmcmdsl", 0); |
3531 | sc->sc_wantresp = ring->qid << 16 | ring->cur; | | 3531 | sc->sc_wantresp = ring->qid << 16 | ring->cur; |
3532 | DPRINTFN(12, ("wantresp is %x\n", sc->sc_wantresp)); | | 3532 | DPRINTFN(12, ("wantresp is %x\n", sc->sc_wantresp)); |
3533 | } | | 3533 | } |
3534 | | | 3534 | |
3535 | /* | | 3535 | /* |
3536 | * Is the hardware still available? (after e.g. above wait). | | 3536 | * Is the hardware still available? (after e.g. above wait). |
3537 | */ | | 3537 | */ |
3538 | s = splnet(); | | 3538 | s = splnet(); |
3539 | if (sc->sc_flags & IWM_FLAG_STOPPED) { | | 3539 | if (sc->sc_flags & IWM_FLAG_STOPPED) { |
3540 | error = ENXIO; | | 3540 | error = ENXIO; |
3541 | goto out; | | 3541 | goto out; |
3542 | } | | 3542 | } |
3543 | | | 3543 | |
3544 | desc = &ring->desc[ring->cur]; | | 3544 | desc = &ring->desc[ring->cur]; |
3545 | data = &ring->data[ring->cur]; | | 3545 | data = &ring->data[ring->cur]; |
3546 | | | 3546 | |
3547 | if (paylen > sizeof(cmd->data)) { | | 3547 | if (paylen > sizeof(cmd->data)) { |
3548 | /* Command is too large */ | | 3548 | /* Command is too large */ |
3549 | if (sizeof(cmd->hdr) + paylen > IWM_RBUF_SIZE) { | | 3549 | if (sizeof(cmd->hdr) + paylen > IWM_RBUF_SIZE) { |
3550 | error = EINVAL; | | 3550 | error = EINVAL; |
3551 | goto out; | | 3551 | goto out; |
3552 | } | | 3552 | } |
3553 | m = m_gethdr(M_DONTWAIT, MT_DATA); | | 3553 | m = m_gethdr(M_DONTWAIT, MT_DATA); |
3554 | if (m == NULL) { | | 3554 | if (m == NULL) { |
3555 | error = ENOMEM; | | 3555 | error = ENOMEM; |
3556 | goto out; | | 3556 | goto out; |
3557 | } | | 3557 | } |
3558 | MEXTMALLOC(m, IWM_RBUF_SIZE, M_DONTWAIT); | | 3558 | MEXTMALLOC(m, IWM_RBUF_SIZE, M_DONTWAIT); |
3559 | if (!(m->m_flags & M_EXT)) { | | 3559 | if (!(m->m_flags & M_EXT)) { |
3560 | m_freem(m); | | 3560 | m_freem(m); |
3561 | error = ENOMEM; | | 3561 | error = ENOMEM; |
3562 | goto out; | | 3562 | goto out; |
3563 | } | | 3563 | } |
3564 | cmd = mtod(m, struct iwm_device_cmd *); | | 3564 | cmd = mtod(m, struct iwm_device_cmd *); |
3565 | error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, | | 3565 | error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, |
3566 | IWM_RBUF_SIZE, NULL, BUS_DMA_NOWAIT | BUS_DMA_WRITE); | | 3566 | IWM_RBUF_SIZE, NULL, BUS_DMA_NOWAIT | BUS_DMA_WRITE); |
3567 | if (error != 0) { | | 3567 | if (error != 0) { |
3568 | m_freem(m); | | 3568 | m_freem(m); |
3569 | goto out; | | 3569 | goto out; |
3570 | } | | 3570 | } |
3571 | data->m = m; | | 3571 | data->m = m; |
3572 | paddr = data->map->dm_segs[0].ds_addr; | | 3572 | paddr = data->map->dm_segs[0].ds_addr; |
3573 | } else { | | 3573 | } else { |
3574 | cmd = &ring->cmd[ring->cur]; | | 3574 | cmd = &ring->cmd[ring->cur]; |
3575 | paddr = data->cmd_paddr; | | 3575 | paddr = data->cmd_paddr; |
3576 | } | | 3576 | } |
3577 | | | 3577 | |
3578 | cmd->hdr.code = code; | | 3578 | cmd->hdr.code = code; |
3579 | cmd->hdr.flags = 0; | | 3579 | cmd->hdr.flags = 0; |
3580 | cmd->hdr.qid = ring->qid; | | 3580 | cmd->hdr.qid = ring->qid; |
3581 | cmd->hdr.idx = ring->cur; | | 3581 | cmd->hdr.idx = ring->cur; |
3582 | | | 3582 | |
3583 | for (i = 0, off = 0; i < __arraycount(hcmd->data); i++) { | | 3583 | for (i = 0, off = 0; i < __arraycount(hcmd->data); i++) { |
3584 | if (hcmd->len[i] == 0) | | 3584 | if (hcmd->len[i] == 0) |
3585 | continue; | | 3585 | continue; |
3586 | memcpy(cmd->data + off, hcmd->data[i], hcmd->len[i]); | | 3586 | memcpy(cmd->data + off, hcmd->data[i], hcmd->len[i]); |
3587 | off += hcmd->len[i]; | | 3587 | off += hcmd->len[i]; |
3588 | } | | 3588 | } |
3589 | KASSERT(off == paylen); | | 3589 | KASSERT(off == paylen); |
3590 | | | 3590 | |
3591 | /* lo field is not aligned */ | | 3591 | /* lo field is not aligned */ |
3592 | addr_lo = htole32((uint32_t)paddr); | | 3592 | addr_lo = htole32((uint32_t)paddr); |
3593 | memcpy(&desc->tbs[0].lo, &addr_lo, sizeof(uint32_t)); | | 3593 | memcpy(&desc->tbs[0].lo, &addr_lo, sizeof(uint32_t)); |
3594 | desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(paddr) | | 3594 | desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(paddr) |
3595 | | ((sizeof(cmd->hdr) + paylen) << 4)); | | 3595 | | ((sizeof(cmd->hdr) + paylen) << 4)); |
3596 | desc->num_tbs = 1; | | 3596 | desc->num_tbs = 1; |
3597 | | | 3597 | |
3598 | DPRINTFN(8, ("iwm_send_cmd 0x%x size=%zu %s\n", | | 3598 | DPRINTFN(8, ("iwm_send_cmd 0x%x size=%zu %s\n", |
3599 | code, sizeof(cmd->hdr) + paylen, async ? " (async)" : "")); | | 3599 | code, sizeof(cmd->hdr) + paylen, async ? " (async)" : "")); |
3600 | | | 3600 | |
3601 | if (paylen > sizeof(cmd->data)) { | | 3601 | if (paylen > sizeof(cmd->data)) { |
3602 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, | | 3602 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, |
3603 | sizeof(cmd->hdr) + paylen, BUS_DMASYNC_PREWRITE); | | 3603 | sizeof(cmd->hdr) + paylen, BUS_DMASYNC_PREWRITE); |
3604 | } else { | | 3604 | } else { |
3605 | bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, | | 3605 | bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, |
3606 | (char *)(void *)cmd - (char *)(void *)ring->cmd_dma.vaddr, | | 3606 | (char *)(void *)cmd - (char *)(void *)ring->cmd_dma.vaddr, |
3607 | sizeof(cmd->hdr) + paylen, BUS_DMASYNC_PREWRITE); | | 3607 | sizeof(cmd->hdr) + paylen, BUS_DMASYNC_PREWRITE); |
3608 | } | | 3608 | } |
3609 | bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, | | 3609 | bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, |
3610 | (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, | | 3610 | (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, |
3611 | sizeof (*desc), BUS_DMASYNC_PREWRITE); | | 3611 | sizeof (*desc), BUS_DMASYNC_PREWRITE); |
3612 | | | 3612 | |
3613 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, | | 3613 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, |
3614 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | | 3614 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); |
3615 | if (!iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, | | 3615 | if (!iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, |
3616 | IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, | | 3616 | IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, |
3617 | (IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | | | 3617 | (IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | |
3618 | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000)) { | | 3618 | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000)) { |
3619 | DPRINTF(("%s: acquiring device failed\n", DEVNAME(sc))); | | 3619 | DPRINTF(("%s: acquiring device failed\n", DEVNAME(sc))); |
3620 | error = EBUSY; | | 3620 | error = EBUSY; |
3621 | goto out; | | 3621 | goto out; |
3622 | } | | 3622 | } |
3623 | | | 3623 | |
3624 | #if 0 | | 3624 | #if 0 |
3625 | iwm_update_sched(sc, ring->qid, ring->cur, 0, 0); | | 3625 | iwm_update_sched(sc, ring->qid, ring->cur, 0, 0); |
3626 | #endif | | 3626 | #endif |
3627 | DPRINTF(("sending command 0x%x qid %d, idx %d\n", | | 3627 | DPRINTF(("sending command 0x%x qid %d, idx %d\n", |
3628 | code, ring->qid, ring->cur)); | | 3628 | code, ring->qid, ring->cur)); |
3629 | | | 3629 | |
3630 | /* Kick command ring. */ | | 3630 | /* Kick command ring. */ |
3631 | ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; | | 3631 | ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; |
3632 | IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); | | 3632 | IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); |
3633 | | | 3633 | |
3634 | if (!async) { | | 3634 | if (!async) { |
3635 | /* m..m-mmyy-mmyyyy-mym-ym m-my generation */ | | 3635 | /* m..m-mmyy-mmyyyy-mym-ym m-my generation */ |
3636 | int generation = sc->sc_generation; | | 3636 | int generation = sc->sc_generation; |
3637 | error = tsleep(desc, PCATCH, "iwmcmd", hz); | | 3637 | error = tsleep(desc, PCATCH, "iwmcmd", hz); |
3638 | if (error == 0) { | | 3638 | if (error == 0) { |
3639 | /* if hardware is no longer up, return error */ | | 3639 | /* if hardware is no longer up, return error */ |
3640 | if (generation != sc->sc_generation) { | | 3640 | if (generation != sc->sc_generation) { |
3641 | error = ENXIO; | | 3641 | error = ENXIO; |
3642 | } else { | | 3642 | } else { |
3643 | hcmd->resp_pkt = (void *)sc->sc_cmd_resp; | | 3643 | hcmd->resp_pkt = (void *)sc->sc_cmd_resp; |
3644 | } | | 3644 | } |
3645 | } | | 3645 | } |
3646 | } | | 3646 | } |
3647 | out: | | 3647 | out: |
3648 | if (wantresp && error != 0) { | | 3648 | if (wantresp && error != 0) { |
3649 | iwm_free_resp(sc, hcmd); | | 3649 | iwm_free_resp(sc, hcmd); |
3650 | } | | 3650 | } |
3651 | splx(s); | | 3651 | splx(s); |
3652 | | | 3652 | |
3653 | return error; | | 3653 | return error; |
3654 | } | | 3654 | } |
3655 | | | 3655 | |
3656 | /* iwlwifi: mvm/utils.c */ | | 3656 | /* iwlwifi: mvm/utils.c */ |
3657 | static int | | 3657 | static int |
3658 | iwm_mvm_send_cmd_pdu(struct iwm_softc *sc, uint8_t id, | | 3658 | iwm_mvm_send_cmd_pdu(struct iwm_softc *sc, uint8_t id, |
3659 | uint32_t flags, uint16_t len, const void *data) | | 3659 | uint32_t flags, uint16_t len, const void *data) |
3660 | { | | 3660 | { |
3661 | struct iwm_host_cmd cmd = { | | 3661 | struct iwm_host_cmd cmd = { |
3662 | .id = id, | | 3662 | .id = id, |
3663 | .len = { len, }, | | 3663 | .len = { len, }, |
3664 | .data = { data, }, | | 3664 | .data = { data, }, |
3665 | .flags = flags, | | 3665 | .flags = flags, |
3666 | }; | | 3666 | }; |
3667 | | | 3667 | |
3668 | return iwm_send_cmd(sc, &cmd); | | 3668 | return iwm_send_cmd(sc, &cmd); |
3669 | } | | 3669 | } |
3670 | | | 3670 | |
3671 | /* iwlwifi: mvm/utils.c */ | | 3671 | /* iwlwifi: mvm/utils.c */ |
3672 | static int | | 3672 | static int |
3673 | iwm_mvm_send_cmd_status(struct iwm_softc *sc, | | 3673 | iwm_mvm_send_cmd_status(struct iwm_softc *sc, |
3674 | struct iwm_host_cmd *cmd, uint32_t *status) | | 3674 | struct iwm_host_cmd *cmd, uint32_t *status) |
3675 | { | | 3675 | { |
3676 | struct iwm_rx_packet *pkt; | | 3676 | struct iwm_rx_packet *pkt; |
3677 | struct iwm_cmd_response *resp; | | 3677 | struct iwm_cmd_response *resp; |
3678 | int error, resp_len; | | 3678 | int error, resp_len; |
3679 | | | 3679 | |
3680 | //lockdep_assert_held(&mvm->mutex); | | 3680 | //lockdep_assert_held(&mvm->mutex); |
3681 | | | 3681 | |
3682 | KASSERT((cmd->flags & IWM_CMD_WANT_SKB) == 0); | | 3682 | KASSERT((cmd->flags & IWM_CMD_WANT_SKB) == 0); |
3683 | cmd->flags |= IWM_CMD_SYNC | IWM_CMD_WANT_SKB; | | 3683 | cmd->flags |= IWM_CMD_SYNC | IWM_CMD_WANT_SKB; |
3684 | | | 3684 | |
3685 | if ((error = iwm_send_cmd(sc, cmd)) != 0) | | 3685 | if ((error = iwm_send_cmd(sc, cmd)) != 0) |
3686 | return error; | | 3686 | return error; |
3687 | pkt = cmd->resp_pkt; | | 3687 | pkt = cmd->resp_pkt; |
3688 | | | 3688 | |
3689 | /* Can happen if RFKILL is asserted */ | | 3689 | /* Can happen if RFKILL is asserted */ |
3690 | if (!pkt) { | | 3690 | if (!pkt) { |
3691 | error = 0; | | 3691 | error = 0; |
3692 | goto out_free_resp; | | 3692 | goto out_free_resp; |
3693 | } | | 3693 | } |
3694 | | | 3694 | |
3695 | if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { | | 3695 | if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { |
3696 | error = EIO; | | 3696 | error = EIO; |
3697 | goto out_free_resp; | | 3697 | goto out_free_resp; |
3698 | } | | 3698 | } |
3699 | | | 3699 | |
3700 | resp_len = iwm_rx_packet_payload_len(pkt); | | 3700 | resp_len = iwm_rx_packet_payload_len(pkt); |
3701 | if (resp_len != sizeof(*resp)) { | | 3701 | if (resp_len != sizeof(*resp)) { |
3702 | error = EIO; | | 3702 | error = EIO; |
3703 | goto out_free_resp; | | 3703 | goto out_free_resp; |
3704 | } | | 3704 | } |
3705 | | | 3705 | |
3706 | resp = (void *)pkt->data; | | 3706 | resp = (void *)pkt->data; |
3707 | *status = le32toh(resp->status); | | 3707 | *status = le32toh(resp->status); |
3708 | out_free_resp: | | 3708 | out_free_resp: |
3709 | iwm_free_resp(sc, cmd); | | 3709 | iwm_free_resp(sc, cmd); |
3710 | return error; | | 3710 | return error; |
3711 | } | | 3711 | } |
3712 | | | 3712 | |
3713 | /* iwlwifi/mvm/utils.c */ | | 3713 | /* iwlwifi/mvm/utils.c */ |
3714 | static int | | 3714 | static int |
3715 | iwm_mvm_send_cmd_pdu_status(struct iwm_softc *sc, uint8_t id, | | 3715 | iwm_mvm_send_cmd_pdu_status(struct iwm_softc *sc, uint8_t id, |
3716 | uint16_t len, const void *data, uint32_t *status) | | 3716 | uint16_t len, const void *data, uint32_t *status) |
3717 | { | | 3717 | { |
3718 | struct iwm_host_cmd cmd = { | | 3718 | struct iwm_host_cmd cmd = { |
3719 | .id = id, | | 3719 | .id = id, |
3720 | .len = { len, }, | | 3720 | .len = { len, }, |
3721 | .data = { data, }, | | 3721 | .data = { data, }, |
3722 | }; | | 3722 | }; |
3723 | | | 3723 | |
3724 | return iwm_mvm_send_cmd_status(sc, &cmd, status); | | 3724 | return iwm_mvm_send_cmd_status(sc, &cmd, status); |
3725 | } | | 3725 | } |
3726 | | | 3726 | |
3727 | static void | | 3727 | static void |
3728 | iwm_free_resp(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) | | 3728 | iwm_free_resp(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) |
3729 | { | | 3729 | { |
3730 | KASSERT(sc->sc_wantresp != -1); | | 3730 | KASSERT(sc->sc_wantresp != -1); |
3731 | KASSERT((hcmd->flags & (IWM_CMD_WANT_SKB|IWM_CMD_SYNC)) | | 3731 | KASSERT((hcmd->flags & (IWM_CMD_WANT_SKB|IWM_CMD_SYNC)) |
3732 | == (IWM_CMD_WANT_SKB|IWM_CMD_SYNC)); | | 3732 | == (IWM_CMD_WANT_SKB|IWM_CMD_SYNC)); |
3733 | sc->sc_wantresp = -1; | | 3733 | sc->sc_wantresp = -1; |
3734 | wakeup(&sc->sc_wantresp); | | 3734 | wakeup(&sc->sc_wantresp); |
3735 | } | | 3735 | } |
3736 | | | 3736 | |
3737 | /* | | 3737 | /* |
3738 | * Process a "command done" firmware notification. This is where we wakeup | | 3738 | * Process a "command done" firmware notification. This is where we wakeup |
3739 | * processes waiting for a synchronous command completion. | | 3739 | * processes waiting for a synchronous command completion. |
3740 | * from if_iwn | | 3740 | * from if_iwn |
3741 | */ | | 3741 | */ |
3742 | static void | | 3742 | static void |
3743 | iwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt) | | 3743 | iwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt) |
3744 | { | | 3744 | { |
3745 | struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; | | 3745 | struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; |
3746 | struct iwm_tx_data *data; | | 3746 | struct iwm_tx_data *data; |
3747 | | | 3747 | |
3748 | if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) { | | 3748 | if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) { |
3749 | return; /* Not a command ack. */ | | 3749 | return; /* Not a command ack. */ |
3750 | } | | 3750 | } |
3751 | | | 3751 | |
3752 | data = &ring->data[pkt->hdr.idx]; | | 3752 | data = &ring->data[pkt->hdr.idx]; |
3753 | | | 3753 | |
3754 | /* If the command was mapped in an mbuf, free it. */ | | 3754 | /* If the command was mapped in an mbuf, free it. */ |
3755 | if (data->m != NULL) { | | 3755 | if (data->m != NULL) { |
3756 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, | | 3756 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, |
3757 | data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); | | 3757 | data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); |
3758 | bus_dmamap_unload(sc->sc_dmat, data->map); | | 3758 | bus_dmamap_unload(sc->sc_dmat, data->map); |
3759 | m_freem(data->m); | | 3759 | m_freem(data->m); |
3760 | data->m = NULL; | | 3760 | data->m = NULL; |
3761 | } | | 3761 | } |
3762 | wakeup(&ring->desc[pkt->hdr.idx]); | | 3762 | wakeup(&ring->desc[pkt->hdr.idx]); |
3763 | } | | 3763 | } |
3764 | | | 3764 | |
3765 | #if 0 | | 3765 | #if 0 |
3766 | /* | | 3766 | /* |
3767 | * necessary only for block ack mode | | 3767 | * necessary only for block ack mode |
3768 | */ | | 3768 | */ |
3769 | void | | 3769 | void |
3770 | iwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, | | 3770 | iwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, |
3771 | uint16_t len) | | 3771 | uint16_t len) |
3772 | { | | 3772 | { |
3773 | struct iwm_agn_scd_bc_tbl *scd_bc_tbl; | | 3773 | struct iwm_agn_scd_bc_tbl *scd_bc_tbl; |
3774 | uint16_t w_val; | | 3774 | uint16_t w_val; |
3775 | | | 3775 | |
3776 | scd_bc_tbl = sc->sched_dma.vaddr; | | 3776 | scd_bc_tbl = sc->sched_dma.vaddr; |
3777 | | | 3777 | |
3778 | len += 8; /* magic numbers came naturally from paris */ | | 3778 | len += 8; /* magic numbers came naturally from paris */ |
3779 | if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE) | | 3779 | if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE) |
3780 | len = roundup(len, 4) / 4; | | 3780 | len = roundup(len, 4) / 4; |
3781 | | | 3781 | |
3782 | w_val = htole16(sta_id << 12 | len); | | 3782 | w_val = htole16(sta_id << 12 | len); |
3783 | | | 3783 | |
3784 | /* Update TX scheduler. */ | | 3784 | /* Update TX scheduler. */ |
3785 | scd_bc_tbl[qid].tfd_offset[idx] = w_val; | | 3785 | scd_bc_tbl[qid].tfd_offset[idx] = w_val; |
3786 | bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map, | | 3786 | bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map, |
3787 | (char *)(void *)w - (char *)(void *)sc->sched_dma.vaddr, | | 3787 | (char *)(void *)w - (char *)(void *)sc->sched_dma.vaddr, |
3788 | sizeof(uint16_t), BUS_DMASYNC_PREWRITE); | | 3788 | sizeof(uint16_t), BUS_DMASYNC_PREWRITE); |
3789 | | | 3789 | |
3790 | /* I really wonder what this is ?!? */ | | 3790 | /* I really wonder what this is ?!? */ |
3791 | if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) { | | 3791 | if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) { |
3792 | scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val; | | 3792 | scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val; |
3793 | bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map, | | 3793 | bus_dmamap_sync(sc->sc_dmat, sc->sched_dma.map, |
3794 | (char *)(void *)(w + IWM_TFD_QUEUE_SIZE_MAX) - | | 3794 | (char *)(void *)(w + IWM_TFD_QUEUE_SIZE_MAX) - |
3795 | (char *)(void *)sc->sched_dma.vaddr, | | 3795 | (char *)(void *)sc->sched_dma.vaddr, |
3796 | sizeof (uint16_t), BUS_DMASYNC_PREWRITE); | | 3796 | sizeof (uint16_t), BUS_DMASYNC_PREWRITE); |
3797 | } | | 3797 | } |
3798 | } | | 3798 | } |
3799 | #endif | | 3799 | #endif |
3800 | | | 3800 | |
3801 | /* | | 3801 | /* |
3802 | * Fill in various bit for management frames, and leave them | | 3802 | * Fill in various bit for management frames, and leave them |
3803 | * unfilled for data frames (firmware takes care of that). | | 3803 | * unfilled for data frames (firmware takes care of that). |
3804 | * Return the selected TX rate. | | 3804 | * Return the selected TX rate. |
3805 | */ | | 3805 | */ |
3806 | static const struct iwm_rate * | | 3806 | static const struct iwm_rate * |
3807 | iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, | | 3807 | iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, |
3808 | struct ieee80211_frame *wh, struct iwm_tx_cmd *tx) | | 3808 | struct ieee80211_frame *wh, struct iwm_tx_cmd *tx) |
3809 | { | | 3809 | { |
3810 | const struct iwm_rate *rinfo; | | 3810 | const struct iwm_rate *rinfo; |
3811 | int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; | | 3811 | int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; |
3812 | int ridx, rate_flags; | | 3812 | int ridx, rate_flags; |
3813 | int nrates = in->in_ni.ni_rates.rs_nrates; | | 3813 | int nrates = in->in_ni.ni_rates.rs_nrates; |
3814 | | | 3814 | |
3815 | tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; | | 3815 | tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; |
3816 | tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; | | 3816 | tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; |
3817 | | | 3817 | |
3818 | /* for data frames, use RS table */ | | 3818 | /* for data frames, use RS table */ |
3819 | if (type == IEEE80211_FC0_TYPE_DATA) { | | 3819 | if (type == IEEE80211_FC0_TYPE_DATA) { |
3820 | if (sc->sc_fixed_ridx != -1) { | | 3820 | if (sc->sc_ic.ic_fixed_rate != -1) { |
3821 | tx->initial_rate_index = sc->sc_fixed_ridx; | | 3821 | tx->initial_rate_index = sc->sc_fixed_ridx; |
3822 | } else { | | 3822 | } else { |
3823 | tx->initial_rate_index = (nrates-1) - in->in_ni.ni_txrate; | | 3823 | tx->initial_rate_index = (nrates-1) - in->in_ni.ni_txrate; |
3824 | } | | 3824 | } |
3825 | tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); | | 3825 | tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); |
3826 | DPRINTFN(12, ("start with txrate %d\n", tx->initial_rate_index)); | | 3826 | DPRINTFN(12, ("start with txrate %d\n", tx->initial_rate_index)); |
3827 | return &iwm_rates[tx->initial_rate_index]; | | 3827 | return &iwm_rates[tx->initial_rate_index]; |
3828 | } | | 3828 | } |
3829 | | | 3829 | |
3830 | /* for non-data, use the lowest supported rate */ | | 3830 | /* for non-data, use the lowest supported rate */ |
3831 | ridx = in->in_ridx[0]; | | 3831 | ridx = in->in_ridx[0]; |
3832 | rinfo = &iwm_rates[ridx]; | | 3832 | rinfo = &iwm_rates[ridx]; |
3833 | | | 3833 | |
3834 | rate_flags = 1 << IWM_RATE_MCS_ANT_POS; | | 3834 | rate_flags = 1 << IWM_RATE_MCS_ANT_POS; |
3835 | if (IWM_RIDX_IS_CCK(ridx)) | | 3835 | if (IWM_RIDX_IS_CCK(ridx)) |
3836 | rate_flags |= IWM_RATE_MCS_CCK_MSK; | | 3836 | rate_flags |= IWM_RATE_MCS_CCK_MSK; |
3837 | tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); | | 3837 | tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); |
3838 | | | 3838 | |
3839 | return rinfo; | | 3839 | return rinfo; |
3840 | } | | 3840 | } |
3841 | | | 3841 | |
3842 | #define TB0_SIZE 16 | | 3842 | #define TB0_SIZE 16 |
3843 | static int | | 3843 | static int |
3844 | iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) | | 3844 | iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) |
3845 | { | | 3845 | { |
3846 | struct ieee80211com *ic = &sc->sc_ic; | | 3846 | struct ieee80211com *ic = &sc->sc_ic; |
3847 | struct iwm_node *in = (void *)ni; | | 3847 | struct iwm_node *in = (void *)ni; |
3848 | struct iwm_tx_ring *ring; | | 3848 | struct iwm_tx_ring *ring; |
3849 | struct iwm_tx_data *data; | | 3849 | struct iwm_tx_data *data; |
3850 | struct iwm_tfd *desc; | | 3850 | struct iwm_tfd *desc; |
3851 | struct iwm_device_cmd *cmd; | | 3851 | struct iwm_device_cmd *cmd; |
3852 | struct iwm_tx_cmd *tx; | | 3852 | struct iwm_tx_cmd *tx; |
3853 | struct ieee80211_frame *wh; | | 3853 | struct ieee80211_frame *wh; |
3854 | struct ieee80211_key *k = NULL; | | 3854 | struct ieee80211_key *k = NULL; |
3855 | struct mbuf *m1; | | 3855 | struct mbuf *m1; |
3856 | const struct iwm_rate *rinfo; | | 3856 | const struct iwm_rate *rinfo; |
3857 | uint32_t flags; | | 3857 | uint32_t flags; |
3858 | u_int hdrlen; | | 3858 | u_int hdrlen; |
3859 | bus_dma_segment_t *seg; | | 3859 | bus_dma_segment_t *seg; |
3860 | uint8_t tid, type; | | 3860 | uint8_t tid, type; |
3861 | int i, totlen, error, pad; | | 3861 | int i, totlen, error, pad; |
3862 | int hdrlen2; | | 3862 | int hdrlen2; |
3863 | | | 3863 | |
3864 | wh = mtod(m, struct ieee80211_frame *); | | 3864 | wh = mtod(m, struct ieee80211_frame *); |
3865 | hdrlen = ieee80211_anyhdrsize(wh); | | 3865 | hdrlen = ieee80211_anyhdrsize(wh); |
3866 | type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; | | 3866 | type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; |
3867 | | | 3867 | |
3868 | hdrlen2 = (ieee80211_has_qos(wh)) ? | | 3868 | hdrlen2 = (ieee80211_has_qos(wh)) ? |
3869 | sizeof (struct ieee80211_qosframe) : | | 3869 | sizeof (struct ieee80211_qosframe) : |
3870 | sizeof (struct ieee80211_frame); | | 3870 | sizeof (struct ieee80211_frame); |
3871 | | | 3871 | |
3872 | if (hdrlen != hdrlen2) | | 3872 | if (hdrlen != hdrlen2) |
3873 | DPRINTF(("%s: hdrlen error (%d != %d)\n", | | 3873 | DPRINTF(("%s: hdrlen error (%d != %d)\n", |
3874 | DEVNAME(sc), hdrlen, hdrlen2)); | | 3874 | DEVNAME(sc), hdrlen, hdrlen2)); |
3875 | | | 3875 | |
3876 | tid = 0; | | 3876 | tid = 0; |
3877 | | | 3877 | |
3878 | ring = &sc->txq[ac]; | | 3878 | ring = &sc->txq[ac]; |
3879 | desc = &ring->desc[ring->cur]; | | 3879 | desc = &ring->desc[ring->cur]; |
3880 | memset(desc, 0, sizeof(*desc)); | | 3880 | memset(desc, 0, sizeof(*desc)); |
3881 | data = &ring->data[ring->cur]; | | 3881 | data = &ring->data[ring->cur]; |
3882 | | | 3882 | |
3883 | /* Fill out iwm_tx_cmd to send to the firmware */ | | 3883 | /* Fill out iwm_tx_cmd to send to the firmware */ |
3884 | cmd = &ring->cmd[ring->cur]; | | 3884 | cmd = &ring->cmd[ring->cur]; |
3885 | cmd->hdr.code = IWM_TX_CMD; | | 3885 | cmd->hdr.code = IWM_TX_CMD; |
3886 | cmd->hdr.flags = 0; | | 3886 | cmd->hdr.flags = 0; |
3887 | cmd->hdr.qid = ring->qid; | | 3887 | cmd->hdr.qid = ring->qid; |
3888 | cmd->hdr.idx = ring->cur; | | 3888 | cmd->hdr.idx = ring->cur; |
3889 | | | 3889 | |
3890 | tx = (void *)cmd->data; | | 3890 | tx = (void *)cmd->data; |
3891 | memset(tx, 0, sizeof(*tx)); | | 3891 | memset(tx, 0, sizeof(*tx)); |
3892 | | | 3892 | |
3893 | rinfo = iwm_tx_fill_cmd(sc, in, wh, tx); | | 3893 | rinfo = iwm_tx_fill_cmd(sc, in, wh, tx); |
3894 | | | 3894 | |
3895 | if (sc->sc_drvbpf != NULL) { | | 3895 | if (sc->sc_drvbpf != NULL) { |
3896 | struct iwm_tx_radiotap_header *tap = &sc->sc_txtap; | | 3896 | struct iwm_tx_radiotap_header *tap = &sc->sc_txtap; |
3897 | | | 3897 | |
3898 | tap->wt_flags = 0; | | 3898 | tap->wt_flags = 0; |
3899 | tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); | | 3899 | tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); |
3900 | tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); | | 3900 | tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); |
3901 | tap->wt_rate = rinfo->rate; | | 3901 | tap->wt_rate = rinfo->rate; |
3902 | tap->wt_hwqueue = ac; | | 3902 | tap->wt_hwqueue = ac; |
3903 | if (wh->i_fc[1] & IEEE80211_FC1_WEP) | | 3903 | if (wh->i_fc[1] & IEEE80211_FC1_WEP) |
3904 | tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; | | 3904 | tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; |
3905 | | | 3905 | |
3906 | bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m); | | 3906 | bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m); |
3907 | } | | 3907 | } |
3908 | | | 3908 | |
3909 | /* Encrypt the frame if need be. */ | | 3909 | /* Encrypt the frame if need be. */ |
3910 | if (wh->i_fc[1] & IEEE80211_FC1_WEP) { | | 3910 | if (wh->i_fc[1] & IEEE80211_FC1_WEP) { |
3911 | k = ieee80211_crypto_encap(ic, ni, m); | | 3911 | k = ieee80211_crypto_encap(ic, ni, m); |
3912 | if (k == NULL) { | | 3912 | if (k == NULL) { |
3913 | m_freem(m); | | 3913 | m_freem(m); |
3914 | return ENOBUFS; | | 3914 | return ENOBUFS; |
3915 | } | | 3915 | } |
3916 | /* Packet header may have moved, reset our local pointer. */ | | 3916 | /* Packet header may have moved, reset our local pointer. */ |
3917 | wh = mtod(m, struct ieee80211_frame *); | | 3917 | wh = mtod(m, struct ieee80211_frame *); |
3918 | } | | 3918 | } |
3919 | totlen = m->m_pkthdr.len; | | 3919 | totlen = m->m_pkthdr.len; |
3920 | | | 3920 | |
3921 | flags = 0; | | 3921 | flags = 0; |
3922 | if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { | | 3922 | if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { |
3923 | flags |= IWM_TX_CMD_FLG_ACK; | | 3923 | flags |= IWM_TX_CMD_FLG_ACK; |
3924 | } | | 3924 | } |
3925 | | | 3925 | |
3926 | if (type != IEEE80211_FC0_TYPE_DATA | | 3926 | if (type != IEEE80211_FC0_TYPE_DATA |
3927 | && (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) | | 3927 | && (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) |
3928 | && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { | | 3928 | && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { |
3929 | flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; | | 3929 | flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; |
3930 | } | | 3930 | } |
3931 | | | 3931 | |
3932 | if (IEEE80211_IS_MULTICAST(wh->i_addr1) || | | 3932 | if (IEEE80211_IS_MULTICAST(wh->i_addr1) || |
3933 | type != IEEE80211_FC0_TYPE_DATA) | | 3933 | type != IEEE80211_FC0_TYPE_DATA) |
3934 | tx->sta_id = sc->sc_aux_sta.sta_id; | | 3934 | tx->sta_id = sc->sc_aux_sta.sta_id; |
3935 | else | | 3935 | else |
3936 | tx->sta_id = IWM_STATION_ID; | | 3936 | tx->sta_id = IWM_STATION_ID; |
3937 | | | 3937 | |
3938 | if (type == IEEE80211_FC0_TYPE_MGT) { | | 3938 | if (type == IEEE80211_FC0_TYPE_MGT) { |
3939 | uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; | | 3939 | uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
3940 | | | 3940 | |
3941 | if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || | | 3941 | if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || |
3942 | subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) | | 3942 | subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) |
3943 | tx->pm_frame_timeout = htole16(3); | | 3943 | tx->pm_frame_timeout = htole16(3); |
3944 | else | | 3944 | else |
3945 | tx->pm_frame_timeout = htole16(2); | | 3945 | tx->pm_frame_timeout = htole16(2); |
3946 | } else { | | 3946 | } else { |
3947 | tx->pm_frame_timeout = htole16(0); | | 3947 | tx->pm_frame_timeout = htole16(0); |
3948 | } | | 3948 | } |
3949 | | | 3949 | |
3950 | if (hdrlen & 3) { | | 3950 | if (hdrlen & 3) { |
3951 | /* First segment length must be a multiple of 4. */ | | 3951 | /* First segment length must be a multiple of 4. */ |
3952 | flags |= IWM_TX_CMD_FLG_MH_PAD; | | 3952 | flags |= IWM_TX_CMD_FLG_MH_PAD; |
3953 | pad = 4 - (hdrlen & 3); | | 3953 | pad = 4 - (hdrlen & 3); |
3954 | } else | | 3954 | } else |
3955 | pad = 0; | | 3955 | pad = 0; |
3956 | | | 3956 | |
3957 | tx->driver_txop = 0; | | 3957 | tx->driver_txop = 0; |
3958 | tx->next_frame_len = 0; | | 3958 | tx->next_frame_len = 0; |
3959 | | | 3959 | |
3960 | tx->len = htole16(totlen); | | 3960 | tx->len = htole16(totlen); |
3961 | tx->tid_tspec = tid; | | 3961 | tx->tid_tspec = tid; |
3962 | tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); | | 3962 | tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); |
3963 | | | 3963 | |
3964 | /* Set physical address of "scratch area". */ | | 3964 | /* Set physical address of "scratch area". */ |
3965 | tx->dram_lsb_ptr = htole32(data->scratch_paddr); | | 3965 | tx->dram_lsb_ptr = htole32(data->scratch_paddr); |
3966 | tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); | | 3966 | tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); |
3967 | | | 3967 | |
3968 | /* Copy 802.11 header in TX command. */ | | 3968 | /* Copy 802.11 header in TX command. */ |
3969 | memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); | | 3969 | memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); |
3970 | | | 3970 | |
3971 | flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; | | 3971 | flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; |
3972 | | | 3972 | |
3973 | tx->sec_ctl = 0; | | 3973 | tx->sec_ctl = 0; |
3974 | tx->tx_flags |= htole32(flags); | | 3974 | tx->tx_flags |= htole32(flags); |
3975 | | | 3975 | |
3976 | /* Trim 802.11 header. */ | | 3976 | /* Trim 802.11 header. */ |
3977 | m_adj(m, hdrlen); | | 3977 | m_adj(m, hdrlen); |
3978 | | | 3978 | |
3979 | error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, | | 3979 | error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, |
3980 | BUS_DMA_NOWAIT | BUS_DMA_WRITE); | | 3980 | BUS_DMA_NOWAIT | BUS_DMA_WRITE); |
3981 | if (error != 0) { | | 3981 | if (error != 0) { |
3982 | if (error != EFBIG) { | | 3982 | if (error != EFBIG) { |
3983 | aprint_error_dev(sc->sc_dev, | | 3983 | aprint_error_dev(sc->sc_dev, |
3984 | "can't map mbuf (error %d)\n", error); | | 3984 | "can't map mbuf (error %d)\n", error); |
3985 | m_freem(m); | | 3985 | m_freem(m); |
3986 | return error; | | 3986 | return error; |
3987 | } | | 3987 | } |
3988 | /* Too many DMA segments, linearize mbuf. */ | | 3988 | /* Too many DMA segments, linearize mbuf. */ |
3989 | MGETHDR(m1, M_DONTWAIT, MT_DATA); | | 3989 | MGETHDR(m1, M_DONTWAIT, MT_DATA); |
3990 | if (m1 == NULL) { | | 3990 | if (m1 == NULL) { |
3991 | m_freem(m); | | 3991 | m_freem(m); |
3992 | return ENOBUFS; | | 3992 | return ENOBUFS; |
3993 | } | | 3993 | } |
3994 | if (m->m_pkthdr.len > MHLEN) { | | 3994 | if (m->m_pkthdr.len > MHLEN) { |
3995 | MCLGET(m1, M_DONTWAIT); | | 3995 | MCLGET(m1, M_DONTWAIT); |
3996 | if (!(m1->m_flags & M_EXT)) { | | 3996 | if (!(m1->m_flags & M_EXT)) { |
3997 | m_freem(m); | | 3997 | m_freem(m); |
3998 | m_freem(m1); | | 3998 | m_freem(m1); |
3999 | return ENOBUFS; | | 3999 | return ENOBUFS; |
4000 | } | | 4000 | } |
4001 | } | | 4001 | } |
4002 | m_copydata(m, 0, m->m_pkthdr.len, mtod(m1, void *)); | | 4002 | m_copydata(m, 0, m->m_pkthdr.len, mtod(m1, void *)); |
4003 | m1->m_pkthdr.len = m1->m_len = m->m_pkthdr.len; | | 4003 | m1->m_pkthdr.len = m1->m_len = m->m_pkthdr.len; |
4004 | m_freem(m); | | 4004 | m_freem(m); |
4005 | m = m1; | | 4005 | m = m1; |
4006 | | | 4006 | |
4007 | error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, | | 4007 | error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m, |
4008 | BUS_DMA_NOWAIT | BUS_DMA_WRITE); | | 4008 | BUS_DMA_NOWAIT | BUS_DMA_WRITE); |
4009 | if (error != 0) { | | 4009 | if (error != 0) { |
4010 | aprint_error_dev(sc->sc_dev, | | 4010 | aprint_error_dev(sc->sc_dev, |
4011 | "can't map mbuf (error %d)\n", error); | | 4011 | "can't map mbuf (error %d)\n", error); |
4012 | m_freem(m); | | 4012 | m_freem(m); |
4013 | return error; | | 4013 | return error; |
4014 | } | | 4014 | } |
4015 | } | | 4015 | } |
4016 | data->m = m; | | 4016 | data->m = m; |
4017 | data->in = in; | | 4017 | data->in = in; |
4018 | data->done = 0; | | 4018 | data->done = 0; |
4019 | | | 4019 | |
4020 | DPRINTFN(8, ("sending txd %p, in %p\n", data, data->in)); | | 4020 | DPRINTFN(8, ("sending txd %p, in %p\n", data, data->in)); |
4021 | KASSERT(data->in != NULL); | | 4021 | KASSERT(data->in != NULL); |
4022 | | | 4022 | |
4023 | DPRINTFN(8, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", | | 4023 | DPRINTFN(8, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", |
4024 | ring->qid, ring->cur, totlen, data->map->dm_nsegs)); | | 4024 | ring->qid, ring->cur, totlen, data->map->dm_nsegs)); |
4025 | | | 4025 | |
4026 | /* Fill TX descriptor. */ | | 4026 | /* Fill TX descriptor. */ |
4027 | desc->num_tbs = 2 + data->map->dm_nsegs; | | 4027 | desc->num_tbs = 2 + data->map->dm_nsegs; |
4028 | | | 4028 | |
4029 | desc->tbs[0].lo = htole32(data->cmd_paddr); | | 4029 | desc->tbs[0].lo = htole32(data->cmd_paddr); |
4030 | desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | | | 4030 | desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | |
4031 | (TB0_SIZE << 4); | | 4031 | (TB0_SIZE << 4); |
4032 | desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE); | | 4032 | desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE); |
4033 | desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | | | 4033 | desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | |
4034 | ((sizeof(struct iwm_cmd_header) + sizeof(*tx) | | 4034 | ((sizeof(struct iwm_cmd_header) + sizeof(*tx) |
4035 | + hdrlen + pad - TB0_SIZE) << 4); | | 4035 | + hdrlen + pad - TB0_SIZE) << 4); |
4036 | | | 4036 | |
4037 | /* Other DMA segments are for data payload. */ | | 4037 | /* Other DMA segments are for data payload. */ |
4038 | seg = data->map->dm_segs; | | 4038 | seg = data->map->dm_segs; |
4039 | for (i = 0; i < data->map->dm_nsegs; i++, seg++) { | | 4039 | for (i = 0; i < data->map->dm_nsegs; i++, seg++) { |
4040 | desc->tbs[i+2].lo = htole32(seg->ds_addr); | | 4040 | desc->tbs[i+2].lo = htole32(seg->ds_addr); |
4041 | desc->tbs[i+2].hi_n_len = \ | | 4041 | desc->tbs[i+2].hi_n_len = \ |
4042 | htole16(iwm_get_dma_hi_addr(seg->ds_addr)) | | 4042 | htole16(iwm_get_dma_hi_addr(seg->ds_addr)) |
4043 | | ((seg->ds_len) << 4); | | 4043 | | ((seg->ds_len) << 4); |
4044 | } | | 4044 | } |
4045 | | | 4045 | |
4046 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, data->map->dm_mapsize, | | 4046 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, data->map->dm_mapsize, |
4047 | BUS_DMASYNC_PREWRITE); | | 4047 | BUS_DMASYNC_PREWRITE); |
4048 | bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, | | 4048 | bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, |
4049 | (char *)(void *)cmd - (char *)(void *)ring->cmd_dma.vaddr, | | 4049 | (char *)(void *)cmd - (char *)(void *)ring->cmd_dma.vaddr, |
4050 | sizeof (*cmd), BUS_DMASYNC_PREWRITE); | | 4050 | sizeof (*cmd), BUS_DMASYNC_PREWRITE); |
4051 | bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, | | 4051 | bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, |
4052 | (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, | | 4052 | (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, |
4053 | sizeof (*desc), BUS_DMASYNC_PREWRITE); | | 4053 | sizeof (*desc), BUS_DMASYNC_PREWRITE); |
4054 | | | 4054 | |
4055 | #if 0 | | 4055 | #if 0 |
4056 | iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len)); | | 4056 | iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len)); |
4057 | #endif | | 4057 | #endif |
4058 | | | 4058 | |
4059 | /* Kick TX ring. */ | | 4059 | /* Kick TX ring. */ |
4060 | ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; | | 4060 | ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; |
4061 | IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); | | 4061 | IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); |
4062 | | | 4062 | |
4063 | /* Mark TX ring as full if we reach a certain threshold. */ | | 4063 | /* Mark TX ring as full if we reach a certain threshold. */ |
4064 | if (++ring->queued > IWM_TX_RING_HIMARK) { | | 4064 | if (++ring->queued > IWM_TX_RING_HIMARK) { |
4065 | sc->qfullmsk |= 1 << ring->qid; | | 4065 | sc->qfullmsk |= 1 << ring->qid; |
4066 | } | | 4066 | } |
4067 | | | 4067 | |
4068 | return 0; | | 4068 | return 0; |
4069 | } | | 4069 | } |
4070 | | | 4070 | |
4071 | #if 0 | | 4071 | #if 0 |
4072 | /* not necessary? */ | | 4072 | /* not necessary? */ |
4073 | static int | | 4073 | static int |
4074 | iwm_mvm_flush_tx_path(struct iwm_softc *sc, int tfd_msk, int sync) | | 4074 | iwm_mvm_flush_tx_path(struct iwm_softc *sc, int tfd_msk, int sync) |
4075 | { | | 4075 | { |
4076 | struct iwm_tx_path_flush_cmd flush_cmd = { | | 4076 | struct iwm_tx_path_flush_cmd flush_cmd = { |
4077 | .queues_ctl = htole32(tfd_msk), | | 4077 | .queues_ctl = htole32(tfd_msk), |
4078 | .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), | | 4078 | .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), |
4079 | }; | | 4079 | }; |
4080 | int ret; | | 4080 | int ret; |
4081 | | | 4081 | |
4082 | ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, | | 4082 | ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, |
4083 | sync ? IWM_CMD_SYNC : IWM_CMD_ASYNC, | | 4083 | sync ? IWM_CMD_SYNC : IWM_CMD_ASYNC, |
4084 | sizeof(flush_cmd), &flush_cmd); | | 4084 | sizeof(flush_cmd), &flush_cmd); |
4085 | if (ret) | | 4085 | if (ret) |
4086 | aprint_error_dev(sc->sc_dev, "Flushing tx queue failed: %d\n", | | 4086 | aprint_error_dev(sc->sc_dev, "Flushing tx queue failed: %d\n", |
4087 | ret); | | 4087 | ret); |
4088 | return ret; | | 4088 | return ret; |
4089 | } | | 4089 | } |
4090 | #endif | | 4090 | #endif |
4091 | | | 4091 | |
4092 | | | 4092 | |
4093 | /* | | 4093 | /* |
4094 | * BEGIN mvm/power.c | | 4094 | * BEGIN mvm/power.c |
4095 | */ | | 4095 | */ |
4096 | | | 4096 | |
4097 | #define IWM_POWER_KEEP_ALIVE_PERIOD_SEC 25 | | 4097 | #define IWM_POWER_KEEP_ALIVE_PERIOD_SEC 25 |
4098 | | | 4098 | |
4099 | static int | | 4099 | static int |
4100 | iwm_mvm_beacon_filter_send_cmd(struct iwm_softc *sc, | | 4100 | iwm_mvm_beacon_filter_send_cmd(struct iwm_softc *sc, |
4101 | struct iwm_beacon_filter_cmd *cmd) | | 4101 | struct iwm_beacon_filter_cmd *cmd) |
4102 | { | | 4102 | { |
4103 | int ret; | | 4103 | int ret; |
4104 | | | 4104 | |
4105 | ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_BEACON_FILTERING_CMD, | | 4105 | ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_BEACON_FILTERING_CMD, |
4106 | IWM_CMD_SYNC, sizeof(struct iwm_beacon_filter_cmd), cmd); | | 4106 | IWM_CMD_SYNC, sizeof(struct iwm_beacon_filter_cmd), cmd); |
4107 | | | 4107 | |
4108 | if (!ret) { | | 4108 | if (!ret) { |
4109 | DPRINTF(("ba_enable_beacon_abort is: %d\n", | | 4109 | DPRINTF(("ba_enable_beacon_abort is: %d\n", |
4110 | le32toh(cmd->ba_enable_beacon_abort))); | | 4110 | le32toh(cmd->ba_enable_beacon_abort))); |
4111 | DPRINTF(("ba_escape_timer is: %d\n", | | 4111 | DPRINTF(("ba_escape_timer is: %d\n", |
4112 | le32toh(cmd->ba_escape_timer))); | | 4112 | le32toh(cmd->ba_escape_timer))); |
4113 | DPRINTF(("bf_debug_flag is: %d\n", | | 4113 | DPRINTF(("bf_debug_flag is: %d\n", |
4114 | le32toh(cmd->bf_debug_flag))); | | 4114 | le32toh(cmd->bf_debug_flag))); |
4115 | DPRINTF(("bf_enable_beacon_filter is: %d\n", | | 4115 | DPRINTF(("bf_enable_beacon_filter is: %d\n", |
4116 | le32toh(cmd->bf_enable_beacon_filter))); | | 4116 | le32toh(cmd->bf_enable_beacon_filter))); |
4117 | DPRINTF(("bf_energy_delta is: %d\n", | | 4117 | DPRINTF(("bf_energy_delta is: %d\n", |
4118 | le32toh(cmd->bf_energy_delta))); | | 4118 | le32toh(cmd->bf_energy_delta))); |
4119 | DPRINTF(("bf_escape_timer is: %d\n", | | 4119 | DPRINTF(("bf_escape_timer is: %d\n", |
4120 | le32toh(cmd->bf_escape_timer))); | | 4120 | le32toh(cmd->bf_escape_timer))); |
4121 | DPRINTF(("bf_roaming_energy_delta is: %d\n", | | 4121 | DPRINTF(("bf_roaming_energy_delta is: %d\n", |
4122 | le32toh(cmd->bf_roaming_energy_delta))); | | 4122 | le32toh(cmd->bf_roaming_energy_delta))); |
4123 | DPRINTF(("bf_roaming_state is: %d\n", | | 4123 | DPRINTF(("bf_roaming_state is: %d\n", |
4124 | le32toh(cmd->bf_roaming_state))); | | 4124 | le32toh(cmd->bf_roaming_state))); |
4125 | DPRINTF(("bf_temp_threshold is: %d\n", | | 4125 | DPRINTF(("bf_temp_threshold is: %d\n", |
4126 | le32toh(cmd->bf_temp_threshold))); | | 4126 | le32toh(cmd->bf_temp_threshold))); |
4127 | DPRINTF(("bf_temp_fast_filter is: %d\n", | | 4127 | DPRINTF(("bf_temp_fast_filter is: %d\n", |
4128 | le32toh(cmd->bf_temp_fast_filter))); | | 4128 | le32toh(cmd->bf_temp_fast_filter))); |
4129 | DPRINTF(("bf_temp_slow_filter is: %d\n", | | 4129 | DPRINTF(("bf_temp_slow_filter is: %d\n", |
4130 | le32toh(cmd->bf_temp_slow_filter))); | | 4130 | le32toh(cmd->bf_temp_slow_filter))); |
4131 | } | | 4131 | } |
4132 | return ret; | | 4132 | return ret; |
4133 | } | | 4133 | } |
4134 | | | 4134 | |
4135 | static void | | 4135 | static void |
4136 | iwm_mvm_beacon_filter_set_cqm_params(struct iwm_softc *sc, | | 4136 | iwm_mvm_beacon_filter_set_cqm_params(struct iwm_softc *sc, |
4137 | struct iwm_node *in, struct iwm_beacon_filter_cmd *cmd) | | 4137 | struct iwm_node *in, struct iwm_beacon_filter_cmd *cmd) |
4138 | { | | 4138 | { |
4139 | cmd->ba_enable_beacon_abort = htole32(sc->sc_bf.ba_enabled); | | 4139 | cmd->ba_enable_beacon_abort = htole32(sc->sc_bf.ba_enabled); |
4140 | } | | 4140 | } |
4141 | | | 4141 | |
4142 | static int | | 4142 | static int |
4143 | iwm_mvm_update_beacon_abort(struct iwm_softc *sc, struct iwm_node *in, | | 4143 | iwm_mvm_update_beacon_abort(struct iwm_softc *sc, struct iwm_node *in, |
4144 | int enable) | | 4144 | int enable) |
4145 | { | | 4145 | { |
4146 | struct iwm_beacon_filter_cmd cmd = { | | 4146 | struct iwm_beacon_filter_cmd cmd = { |
4147 | IWM_BF_CMD_CONFIG_DEFAULTS, | | 4147 | IWM_BF_CMD_CONFIG_DEFAULTS, |
4148 | .bf_enable_beacon_filter = htole32(1), | | 4148 | .bf_enable_beacon_filter = htole32(1), |
4149 | .ba_enable_beacon_abort = htole32(enable), | | 4149 | .ba_enable_beacon_abort = htole32(enable), |
4150 | }; | | 4150 | }; |
4151 | | | 4151 | |
4152 | if (!sc->sc_bf.bf_enabled) | | 4152 | if (!sc->sc_bf.bf_enabled) |
4153 | return 0; | | 4153 | return 0; |
4154 | | | 4154 | |
4155 | sc->sc_bf.ba_enabled = enable; | | 4155 | sc->sc_bf.ba_enabled = enable; |
4156 | iwm_mvm_beacon_filter_set_cqm_params(sc, in, &cmd); | | 4156 | iwm_mvm_beacon_filter_set_cqm_params(sc, in, &cmd); |
4157 | return iwm_mvm_beacon_filter_send_cmd(sc, &cmd); | | 4157 | return iwm_mvm_beacon_filter_send_cmd(sc, &cmd); |
4158 | } | | 4158 | } |
4159 | | | 4159 | |
4160 | static void | | 4160 | static void |
4161 | iwm_mvm_power_log(struct iwm_softc *sc, struct iwm_mac_power_cmd *cmd) | | 4161 | iwm_mvm_power_log(struct iwm_softc *sc, struct iwm_mac_power_cmd *cmd) |
4162 | { | | 4162 | { |
4163 | DPRINTF(("Sending power table command on mac id 0x%X for " | | 4163 | DPRINTF(("Sending power table command on mac id 0x%X for " |
4164 | "power level %d, flags = 0x%X\n", | | 4164 | "power level %d, flags = 0x%X\n", |
4165 | cmd->id_and_color, IWM_POWER_SCHEME_CAM, le16toh(cmd->flags))); | | 4165 | cmd->id_and_color, IWM_POWER_SCHEME_CAM, le16toh(cmd->flags))); |
4166 | DPRINTF(("Keep alive = %u sec\n", le16toh(cmd->keep_alive_seconds))); | | 4166 | DPRINTF(("Keep alive = %u sec\n", le16toh(cmd->keep_alive_seconds))); |
4167 | | | 4167 | |
4168 | if (!(cmd->flags & htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { | | 4168 | if (!(cmd->flags & htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { |
4169 | DPRINTF(("Disable power management\n")); | | 4169 | DPRINTF(("Disable power management\n")); |
4170 | return; | | 4170 | return; |
4171 | } | | 4171 | } |
4172 | KASSERT(0); | | 4172 | KASSERT(0); |
4173 | | | 4173 | |
4174 | #if 0 | | 4174 | #if 0 |
4175 | DPRINTF(mvm, "Rx timeout = %u usec\n", | | 4175 | DPRINTF(mvm, "Rx timeout = %u usec\n", |
4176 | le32_to_cpu(cmd->rx_data_timeout)); | | 4176 | le32_to_cpu(cmd->rx_data_timeout)); |
4177 | DPRINTF(mvm, "Tx timeout = %u usec\n", | | 4177 | DPRINTF(mvm, "Tx timeout = %u usec\n", |
4178 | le32_to_cpu(cmd->tx_data_timeout)); | | 4178 | le32_to_cpu(cmd->tx_data_timeout)); |
4179 | if (cmd->flags & cpu_to_le16(IWM_POWER_FLAGS_SKIP_OVER_DTIM_MSK)) | | 4179 | if (cmd->flags & cpu_to_le16(IWM_POWER_FLAGS_SKIP_OVER_DTIM_MSK)) |
4180 | DPRINTF(mvm, "DTIM periods to skip = %u\n", | | 4180 | DPRINTF(mvm, "DTIM periods to skip = %u\n", |
4181 | cmd->skip_dtim_periods); | | 4181 | cmd->skip_dtim_periods); |
4182 | if (cmd->flags & cpu_to_le16(IWM_POWER_FLAGS_LPRX_ENA_MSK)) | | 4182 | if (cmd->flags & cpu_to_le16(IWM_POWER_FLAGS_LPRX_ENA_MSK)) |
4183 | DPRINTF(mvm, "LP RX RSSI threshold = %u\n", | | 4183 | DPRINTF(mvm, "LP RX RSSI threshold = %u\n", |
4184 | cmd->lprx_rssi_threshold); | | 4184 | cmd->lprx_rssi_threshold); |
4185 | if (cmd->flags & cpu_to_le16(IWM_POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { | | 4185 | if (cmd->flags & cpu_to_le16(IWM_POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { |
4186 | DPRINTF(mvm, "uAPSD enabled\n"); | | 4186 | DPRINTF(mvm, "uAPSD enabled\n"); |
4187 | DPRINTF(mvm, "Rx timeout (uAPSD) = %u usec\n", | | 4187 | DPRINTF(mvm, "Rx timeout (uAPSD) = %u usec\n", |
4188 | le32_to_cpu(cmd->rx_data_timeout_uapsd)); | | 4188 | le32_to_cpu(cmd->rx_data_timeout_uapsd)); |
4189 | DPRINTF(mvm, "Tx timeout (uAPSD) = %u usec\n", | | 4189 | DPRINTF(mvm, "Tx timeout (uAPSD) = %u usec\n", |
4190 | le32_to_cpu(cmd->tx_data_timeout_uapsd)); | | 4190 | le32_to_cpu(cmd->tx_data_timeout_uapsd)); |
4191 | DPRINTF(mvm, "QNDP TID = %d\n", cmd->qndp_tid); | | 4191 | DPRINTF(mvm, "QNDP TID = %d\n", cmd->qndp_tid); |
4192 | DPRINTF(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags); | | 4192 | DPRINTF(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags); |
4193 | DPRINTF(mvm, "Max SP = %d\n", cmd->uapsd_max_sp); | | 4193 | DPRINTF(mvm, "Max SP = %d\n", cmd->uapsd_max_sp); |
4194 | } | | 4194 | } |
4195 | #endif | | 4195 | #endif |
4196 | } | | 4196 | } |
4197 | | | 4197 | |
4198 | static void | | 4198 | static void |
4199 | iwm_mvm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in, | | 4199 | iwm_mvm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in, |
4200 | struct iwm_mac_power_cmd *cmd) | | 4200 | struct iwm_mac_power_cmd *cmd) |
4201 | { | | 4201 | { |
4202 | struct ieee80211com *ic = &sc->sc_ic; | | 4202 | struct ieee80211com *ic = &sc->sc_ic; |
4203 | struct ieee80211_node *ni = &in->in_ni; | | 4203 | struct ieee80211_node *ni = &in->in_ni; |
4204 | int dtimper, dtimper_msec; | | 4204 | int dtimper, dtimper_msec; |
4205 | int keep_alive; | | 4205 | int keep_alive; |
4206 | | | 4206 | |
4207 | cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, | | 4207 | cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, |
4208 | in->in_color)); | | 4208 | in->in_color)); |
4209 | dtimper = ic->ic_dtim_period ?: 1; | | 4209 | dtimper = ic->ic_dtim_period ?: 1; |
4210 | | | 4210 | |
4211 | /* | | 4211 | /* |
4212 | * Regardless of power management state the driver must set | | 4212 | * Regardless of power management state the driver must set |
4213 | * keep alive period. FW will use it for sending keep alive NDPs | | 4213 | * keep alive period. FW will use it for sending keep alive NDPs |
4214 | * immediately after association. Check that keep alive period | | 4214 | * immediately after association. Check that keep alive period |
4215 | * is at least 3 * DTIM | | 4215 | * is at least 3 * DTIM |
4216 | */ | | 4216 | */ |
4217 | dtimper_msec = dtimper * ni->ni_intval; | | 4217 | dtimper_msec = dtimper * ni->ni_intval; |
4218 | keep_alive | | 4218 | keep_alive |
4219 | = MAX(3 * dtimper_msec, 1000 * IWM_POWER_KEEP_ALIVE_PERIOD_SEC); | | 4219 | = MAX(3 * dtimper_msec, 1000 * IWM_POWER_KEEP_ALIVE_PERIOD_SEC); |
4220 | keep_alive = roundup(keep_alive, 1000) / 1000; | | 4220 | keep_alive = roundup(keep_alive, 1000) / 1000; |
4221 | cmd->keep_alive_seconds = htole16(keep_alive); | | 4221 | cmd->keep_alive_seconds = htole16(keep_alive); |
4222 | } | | 4222 | } |
4223 | | | 4223 | |
4224 | static int | | 4224 | static int |
4225 | iwm_mvm_power_mac_update_mode(struct iwm_softc *sc, struct iwm_node *in) | | 4225 | iwm_mvm_power_mac_update_mode(struct iwm_softc *sc, struct iwm_node *in) |
4226 | { | | 4226 | { |
4227 | int ret; | | 4227 | int ret; |
4228 | int ba_enable; | | 4228 | int ba_enable; |
4229 | struct iwm_mac_power_cmd cmd; | | 4229 | struct iwm_mac_power_cmd cmd; |
4230 | | | 4230 | |
4231 | memset(&cmd, 0, sizeof(cmd)); | | 4231 | memset(&cmd, 0, sizeof(cmd)); |
4232 | | | 4232 | |
4233 | iwm_mvm_power_build_cmd(sc, in, &cmd); | | 4233 | iwm_mvm_power_build_cmd(sc, in, &cmd); |
4234 | iwm_mvm_power_log(sc, &cmd); | | 4234 | iwm_mvm_power_log(sc, &cmd); |
4235 | | | 4235 | |
4236 | if ((ret = iwm_mvm_send_cmd_pdu(sc, IWM_MAC_PM_POWER_TABLE, | | 4236 | if ((ret = iwm_mvm_send_cmd_pdu(sc, IWM_MAC_PM_POWER_TABLE, |
4237 | IWM_CMD_SYNC, sizeof(cmd), &cmd)) != 0) | | 4237 | IWM_CMD_SYNC, sizeof(cmd), &cmd)) != 0) |
4238 | return ret; | | 4238 | return ret; |
4239 | | | 4239 | |
4240 | ba_enable = !!(cmd.flags & | | 4240 | ba_enable = !!(cmd.flags & |
4241 | htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); | | 4241 | htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); |
4242 | return iwm_mvm_update_beacon_abort(sc, in, ba_enable); | | 4242 | return iwm_mvm_update_beacon_abort(sc, in, ba_enable); |
4243 | } | | 4243 | } |
4244 | | | 4244 | |
4245 | static int | | 4245 | static int |
4246 | iwm_mvm_power_update_device(struct iwm_softc *sc) | | 4246 | iwm_mvm_power_update_device(struct iwm_softc *sc) |
4247 | { | | 4247 | { |
4248 | struct iwm_device_power_cmd cmd = { | | 4248 | struct iwm_device_power_cmd cmd = { |
4249 | .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), | | 4249 | .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), |
4250 | }; | | 4250 | }; |
4251 | | | 4251 | |
4252 | if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) | | 4252 | if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) |
4253 | return 0; | | 4253 | return 0; |
4254 | | | 4254 | |
4255 | cmd.flags |= htole16(IWM_DEVICE_POWER_FLAGS_CAM_MSK); | | 4255 | cmd.flags |= htole16(IWM_DEVICE_POWER_FLAGS_CAM_MSK); |
4256 | DPRINTF(("Sending device power command with flags = 0x%X\n", cmd.flags)); | | 4256 | DPRINTF(("Sending device power command with flags = 0x%X\n", cmd.flags)); |
4257 | | | 4257 | |
4258 | return iwm_mvm_send_cmd_pdu(sc, | | 4258 | return iwm_mvm_send_cmd_pdu(sc, |
4259 | IWM_POWER_TABLE_CMD, IWM_CMD_SYNC, sizeof(cmd), &cmd); | | 4259 | IWM_POWER_TABLE_CMD, IWM_CMD_SYNC, sizeof(cmd), &cmd); |
4260 | } | | 4260 | } |
4261 | | | 4261 | |
4262 | static int | | 4262 | static int |
4263 | iwm_mvm_enable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) | | 4263 | iwm_mvm_enable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) |
4264 | { | | 4264 | { |
4265 | struct iwm_beacon_filter_cmd cmd = { | | 4265 | struct iwm_beacon_filter_cmd cmd = { |
4266 | IWM_BF_CMD_CONFIG_DEFAULTS, | | 4266 | IWM_BF_CMD_CONFIG_DEFAULTS, |
4267 | .bf_enable_beacon_filter = htole32(1), | | 4267 | .bf_enable_beacon_filter = htole32(1), |
4268 | }; | | 4268 | }; |
4269 | int ret; | | 4269 | int ret; |
4270 | | | 4270 | |
4271 | iwm_mvm_beacon_filter_set_cqm_params(sc, in, &cmd); | | 4271 | iwm_mvm_beacon_filter_set_cqm_params(sc, in, &cmd); |
4272 | ret = iwm_mvm_beacon_filter_send_cmd(sc, &cmd); | | 4272 | ret = iwm_mvm_beacon_filter_send_cmd(sc, &cmd); |
4273 | | | 4273 | |
4274 | if (ret == 0) | | 4274 | if (ret == 0) |
4275 | sc->sc_bf.bf_enabled = 1; | | 4275 | sc->sc_bf.bf_enabled = 1; |
4276 | | | 4276 | |
4277 | return ret; | | 4277 | return ret; |
4278 | } | | 4278 | } |
4279 | | | 4279 | |
4280 | static int | | 4280 | static int |
4281 | iwm_mvm_disable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) | | 4281 | iwm_mvm_disable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) |
4282 | { | | 4282 | { |
4283 | struct iwm_beacon_filter_cmd cmd; | | 4283 | struct iwm_beacon_filter_cmd cmd; |
4284 | int ret; | | 4284 | int ret; |
4285 | | | 4285 | |
4286 | memset(&cmd, 0, sizeof(cmd)); | | 4286 | memset(&cmd, 0, sizeof(cmd)); |
4287 | if ((sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_BF_UPDATED) == 0) | | 4287 | if ((sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_BF_UPDATED) == 0) |
4288 | return 0; | | 4288 | return 0; |
4289 | | | 4289 | |
4290 | ret = iwm_mvm_beacon_filter_send_cmd(sc, &cmd); | | 4290 | ret = iwm_mvm_beacon_filter_send_cmd(sc, &cmd); |
4291 | if (ret == 0) | | 4291 | if (ret == 0) |
4292 | sc->sc_bf.bf_enabled = 0; | | 4292 | sc->sc_bf.bf_enabled = 0; |
4293 | | | 4293 | |
4294 | return ret; | | 4294 | return ret; |
4295 | } | | 4295 | } |
4296 | | | 4296 | |
4297 | #if 0 | | 4297 | #if 0 |
4298 | static int | | 4298 | static int |
4299 | iwm_mvm_update_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) | | 4299 | iwm_mvm_update_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) |
4300 | { | | 4300 | { |
4301 | if (!sc->sc_bf.bf_enabled) | | 4301 | if (!sc->sc_bf.bf_enabled) |
4302 | return 0; | | 4302 | return 0; |
4303 | | | 4303 | |
4304 | return iwm_mvm_enable_beacon_filter(sc, in); | | 4304 | return iwm_mvm_enable_beacon_filter(sc, in); |
4305 | } | | 4305 | } |
4306 | #endif | | 4306 | #endif |
4307 | | | 4307 | |
4308 | /* | | 4308 | /* |
4309 | * END mvm/power.c | | 4309 | * END mvm/power.c |
4310 | */ | | 4310 | */ |
4311 | | | 4311 | |
4312 | /* | | 4312 | /* |
4313 | * BEGIN mvm/sta.c | | 4313 | * BEGIN mvm/sta.c |
4314 | */ | | 4314 | */ |
4315 | | | 4315 | |
4316 | static void | | 4316 | static void |
4317 | iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *cmd_v6, | | 4317 | iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *cmd_v6, |
4318 | struct iwm_mvm_add_sta_cmd_v5 *cmd_v5) | | 4318 | struct iwm_mvm_add_sta_cmd_v5 *cmd_v5) |
4319 | { | | 4319 | { |
4320 | memset(cmd_v5, 0, sizeof(*cmd_v5)); | | 4320 | memset(cmd_v5, 0, sizeof(*cmd_v5)); |
4321 | | | 4321 | |
4322 | cmd_v5->add_modify = cmd_v6->add_modify; | | 4322 | cmd_v5->add_modify = cmd_v6->add_modify; |
4323 | cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx; | | 4323 | cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx; |
4324 | cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color; | | 4324 | cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color; |
4325 | memcpy(cmd_v5->addr, cmd_v6->addr, ETHER_ADDR_LEN); | | 4325 | memcpy(cmd_v5->addr, cmd_v6->addr, ETHER_ADDR_LEN); |
4326 | cmd_v5->sta_id = cmd_v6->sta_id; | | 4326 | cmd_v5->sta_id = cmd_v6->sta_id; |
4327 | cmd_v5->modify_mask = cmd_v6->modify_mask; | | 4327 | cmd_v5->modify_mask = cmd_v6->modify_mask; |
4328 | cmd_v5->station_flags = cmd_v6->station_flags; | | 4328 | cmd_v5->station_flags = cmd_v6->station_flags; |
4329 | cmd_v5->station_flags_msk = cmd_v6->station_flags_msk; | | 4329 | cmd_v5->station_flags_msk = cmd_v6->station_flags_msk; |
4330 | cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid; | | 4330 | cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid; |
4331 | cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid; | | 4331 | cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid; |
4332 | cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn; | | 4332 | cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn; |
4333 | cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count; | | 4333 | cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count; |
4334 | cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags; | | 4334 | cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags; |
4335 | cmd_v5->assoc_id = cmd_v6->assoc_id; | | 4335 | cmd_v5->assoc_id = cmd_v6->assoc_id; |
4336 | cmd_v5->beamform_flags = cmd_v6->beamform_flags; | | 4336 | cmd_v5->beamform_flags = cmd_v6->beamform_flags; |
4337 | cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk; | | 4337 | cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk; |
4338 | } | | 4338 | } |
4339 | | | 4339 | |
4340 | static int | | 4340 | static int |
4341 | iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc, | | 4341 | iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc, |
4342 | struct iwm_mvm_add_sta_cmd_v6 *cmd, int *status) | | 4342 | struct iwm_mvm_add_sta_cmd_v6 *cmd, int *status) |
4343 | { | | 4343 | { |
4344 | struct iwm_mvm_add_sta_cmd_v5 cmd_v5; | | 4344 | struct iwm_mvm_add_sta_cmd_v5 cmd_v5; |
4345 | | | 4345 | |
4346 | if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_STA_KEY_CMD) { | | 4346 | if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_STA_KEY_CMD) { |
4347 | return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, | | 4347 | return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, |
4348 | sizeof(*cmd), cmd, status); | | 4348 | sizeof(*cmd), cmd, status); |
4349 | } | | 4349 | } |
4350 | | | 4350 | |
4351 | iwm_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); | | 4351 | iwm_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); |
4352 | | | 4352 | |
4353 | return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(cmd_v5), | | 4353 | return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(cmd_v5), |
4354 | &cmd_v5, status); | | 4354 | &cmd_v5, status); |
4355 | } | | 4355 | } |
4356 | | | 4356 | |
4357 | /* send station add/update command to firmware */ | | 4357 | /* send station add/update command to firmware */ |
4358 | static int | | 4358 | static int |
4359 | iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update) | | 4359 | iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update) |
4360 | { | | 4360 | { |
4361 | struct iwm_mvm_add_sta_cmd_v6 add_sta_cmd; | | 4361 | struct iwm_mvm_add_sta_cmd_v6 add_sta_cmd; |
4362 | int ret; | | 4362 | int ret; |
4363 | uint32_t status; | | 4363 | uint32_t status; |
4364 | | | 4364 | |
4365 | memset(&add_sta_cmd, 0, sizeof(add_sta_cmd)); | | 4365 | memset(&add_sta_cmd, 0, sizeof(add_sta_cmd)); |
4366 | | | 4366 | |
4367 | add_sta_cmd.sta_id = IWM_STATION_ID; | | 4367 | add_sta_cmd.sta_id = IWM_STATION_ID; |
4368 | add_sta_cmd.mac_id_n_color | | 4368 | add_sta_cmd.mac_id_n_color |
4369 | = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color)); | | 4369 | = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color)); |
4370 | if (!update) { | | 4370 | if (!update) { |
4371 | add_sta_cmd.tfd_queue_msk = htole32(0xf); | | 4371 | add_sta_cmd.tfd_queue_msk = htole32(0xf); |
4372 | IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); | | 4372 | IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); |
4373 | } | | 4373 | } |
4374 | add_sta_cmd.add_modify = update ? 1 : 0; | | 4374 | add_sta_cmd.add_modify = update ? 1 : 0; |
4375 | add_sta_cmd.station_flags_msk | | 4375 | add_sta_cmd.station_flags_msk |
4376 | |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK); | | 4376 | |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK); |
4377 | | | 4377 | |
4378 | status = IWM_ADD_STA_SUCCESS; | | 4378 | status = IWM_ADD_STA_SUCCESS; |
4379 | ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status); | | 4379 | ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status); |
4380 | if (ret) | | 4380 | if (ret) |
4381 | return ret; | | 4381 | return ret; |
4382 | | | 4382 | |
4383 | switch (status) { | | 4383 | switch (status) { |
4384 | case IWM_ADD_STA_SUCCESS: | | 4384 | case IWM_ADD_STA_SUCCESS: |
4385 | break; | | 4385 | break; |
4386 | default: | | 4386 | default: |
4387 | ret = EIO; | | 4387 | ret = EIO; |
4388 | DPRINTF(("IWM_ADD_STA failed\n")); | | 4388 | DPRINTF(("IWM_ADD_STA failed\n")); |
4389 | break; | | 4389 | break; |
4390 | } | | 4390 | } |
4391 | | | 4391 | |
4392 | return ret; | | 4392 | return ret; |
4393 | } | | 4393 | } |
4394 | | | 4394 | |
4395 | static int | | 4395 | static int |
4396 | iwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in) | | 4396 | iwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in) |
4397 | { | | 4397 | { |
4398 | int ret; | | 4398 | int ret; |
4399 | | | 4399 | |
4400 | ret = iwm_mvm_sta_send_to_fw(sc, in, 0); | | 4400 | ret = iwm_mvm_sta_send_to_fw(sc, in, 0); |
4401 | if (ret) | | 4401 | if (ret) |
4402 | return ret; | | 4402 | return ret; |
4403 | | | 4403 | |
4404 | return 0; | | 4404 | return 0; |
4405 | } | | 4405 | } |
4406 | | | 4406 | |
4407 | static int | | 4407 | static int |
4408 | iwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in) | | 4408 | iwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in) |
4409 | { | | 4409 | { |
4410 | return iwm_mvm_sta_send_to_fw(sc, in, 1); | | 4410 | return iwm_mvm_sta_send_to_fw(sc, in, 1); |
4411 | } | | 4411 | } |
4412 | | | 4412 | |
4413 | static int | | 4413 | static int |
4414 | iwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta, | | 4414 | iwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta, |
4415 | const uint8_t *addr, uint16_t mac_id, uint16_t color) | | 4415 | const uint8_t *addr, uint16_t mac_id, uint16_t color) |
4416 | { | | 4416 | { |
4417 | struct iwm_mvm_add_sta_cmd_v6 cmd; | | 4417 | struct iwm_mvm_add_sta_cmd_v6 cmd; |
4418 | int ret; | | 4418 | int ret; |
4419 | uint32_t status; | | 4419 | uint32_t status; |
4420 | | | 4420 | |
4421 | memset(&cmd, 0, sizeof(cmd)); | | 4421 | memset(&cmd, 0, sizeof(cmd)); |
4422 | cmd.sta_id = sta->sta_id; | | 4422 | cmd.sta_id = sta->sta_id; |
4423 | cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color)); | | 4423 | cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color)); |
4424 | | | 4424 | |
4425 | cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk); | | 4425 | cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk); |
4426 | | | 4426 | |
4427 | if (addr) | | 4427 | if (addr) |
4428 | memcpy(cmd.addr, addr, ETHER_ADDR_LEN); | | 4428 | memcpy(cmd.addr, addr, ETHER_ADDR_LEN); |
4429 | | | 4429 | |
4430 | ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status); | | 4430 | ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status); |
4431 | if (ret) | | 4431 | if (ret) |
4432 | return ret; | | 4432 | return ret; |
4433 | | | 4433 | |
4434 | switch (status) { | | 4434 | switch (status) { |
4435 | case IWM_ADD_STA_SUCCESS: | | 4435 | case IWM_ADD_STA_SUCCESS: |
4436 | DPRINTF(("Internal station added.\n")); | | 4436 | DPRINTF(("Internal station added.\n")); |
4437 | return 0; | | 4437 | return 0; |
4438 | default: | | 4438 | default: |
4439 | DPRINTF(("%s: Add internal station failed, status=0x%x\n", | | 4439 | DPRINTF(("%s: Add internal station failed, status=0x%x\n", |
4440 | DEVNAME(sc), status)); | | 4440 | DEVNAME(sc), status)); |
4441 | ret = EIO; | | 4441 | ret = EIO; |
4442 | break; | | 4442 | break; |
4443 | } | | 4443 | } |
4444 | return ret; | | 4444 | return ret; |
4445 | } | | 4445 | } |
4446 | | | 4446 | |
4447 | static int | | 4447 | static int |
4448 | iwm_mvm_add_aux_sta(struct iwm_softc *sc) | | 4448 | iwm_mvm_add_aux_sta(struct iwm_softc *sc) |
4449 | { | | 4449 | { |
4450 | int ret; | | 4450 | int ret; |
4451 | | | 4451 | |
4452 | sc->sc_aux_sta.sta_id = 3; | | 4452 | sc->sc_aux_sta.sta_id = 3; |
4453 | sc->sc_aux_sta.tfd_queue_msk = 0; | | 4453 | sc->sc_aux_sta.tfd_queue_msk = 0; |
4454 | | | 4454 | |
4455 | ret = iwm_mvm_add_int_sta_common(sc, | | 4455 | ret = iwm_mvm_add_int_sta_common(sc, |
4456 | &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0); | | 4456 | &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0); |
4457 | | | 4457 | |
4458 | if (ret) | | 4458 | if (ret) |
4459 | memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta)); | | 4459 | memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta)); |
4460 | return ret; | | 4460 | return ret; |
4461 | } | | 4461 | } |
4462 | | | 4462 | |
4463 | /* | | 4463 | /* |
4464 | * END mvm/sta.c | | 4464 | * END mvm/sta.c |
4465 | */ | | 4465 | */ |
4466 | | | 4466 | |
4467 | /* | | 4467 | /* |
4468 | * BEGIN mvm/scan.c | | 4468 | * BEGIN mvm/scan.c |
4469 | */ | | 4469 | */ |
4470 | | | 4470 | |
4471 | #define IWM_PLCP_QUIET_THRESH 1 | | 4471 | #define IWM_PLCP_QUIET_THRESH 1 |
4472 | #define IWM_ACTIVE_QUIET_TIME 10 | | 4472 | #define IWM_ACTIVE_QUIET_TIME 10 |
4473 | #define LONG_OUT_TIME_PERIOD 600 | | 4473 | #define LONG_OUT_TIME_PERIOD 600 |
4474 | #define SHORT_OUT_TIME_PERIOD 200 | | 4474 | #define SHORT_OUT_TIME_PERIOD 200 |
4475 | #define SUSPEND_TIME_PERIOD 100 | | 4475 | #define SUSPEND_TIME_PERIOD 100 |
4476 | | | 4476 | |
4477 | static uint16_t | | 4477 | static uint16_t |
4478 | iwm_mvm_scan_rx_chain(struct iwm_softc *sc) | | 4478 | iwm_mvm_scan_rx_chain(struct iwm_softc *sc) |
4479 | { | | 4479 | { |
4480 | uint16_t rx_chain; | | 4480 | uint16_t rx_chain; |
4481 | uint8_t rx_ant; | | 4481 | uint8_t rx_ant; |
4482 | | | 4482 | |
4483 | rx_ant = IWM_FW_VALID_RX_ANT(sc); | | 4483 | rx_ant = IWM_FW_VALID_RX_ANT(sc); |
4484 | rx_chain = rx_ant << IWM_PHY_RX_CHAIN_VALID_POS; | | 4484 | rx_chain = rx_ant << IWM_PHY_RX_CHAIN_VALID_POS; |
4485 | rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; | | 4485 | rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; |
4486 | rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_SEL_POS; | | 4486 | rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_SEL_POS; |
4487 | rx_chain |= 0x1 << IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS; | | 4487 | rx_chain |= 0x1 << IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS; |
4488 | return htole16(rx_chain); | | 4488 | return htole16(rx_chain); |
4489 | } | | 4489 | } |
4490 | | | 4490 | |
4491 | #define ieee80211_tu_to_usec(a) (1024*(a)) | | 4491 | #define ieee80211_tu_to_usec(a) (1024*(a)) |
4492 | | | 4492 | |
4493 | static uint32_t | | 4493 | static uint32_t |
4494 | iwm_mvm_scan_max_out_time(struct iwm_softc *sc, uint32_t flags, int is_assoc) | | 4494 | iwm_mvm_scan_max_out_time(struct iwm_softc *sc, uint32_t flags, int is_assoc) |
4495 | { | | 4495 | { |
4496 | if (!is_assoc) | | 4496 | if (!is_assoc) |
4497 | return 0; | | 4497 | return 0; |
4498 | if (flags & 0x1) | | 4498 | if (flags & 0x1) |
4499 | return htole32(ieee80211_tu_to_usec(SHORT_OUT_TIME_PERIOD)); | | 4499 | return htole32(ieee80211_tu_to_usec(SHORT_OUT_TIME_PERIOD)); |
4500 | return htole32(ieee80211_tu_to_usec(LONG_OUT_TIME_PERIOD)); | | 4500 | return htole32(ieee80211_tu_to_usec(LONG_OUT_TIME_PERIOD)); |
4501 | } | | 4501 | } |
4502 | | | 4502 | |
4503 | static uint32_t | | 4503 | static uint32_t |
4504 | iwm_mvm_scan_suspend_time(struct iwm_softc *sc, int is_assoc) | | 4504 | iwm_mvm_scan_suspend_time(struct iwm_softc *sc, int is_assoc) |
4505 | { | | 4505 | { |
4506 | if (!is_assoc) | | 4506 | if (!is_assoc) |
4507 | return 0; | | 4507 | return 0; |
4508 | return htole32(ieee80211_tu_to_usec(SUSPEND_TIME_PERIOD)); | | 4508 | return htole32(ieee80211_tu_to_usec(SUSPEND_TIME_PERIOD)); |
4509 | } | | 4509 | } |
4510 | | | 4510 | |
4511 | static uint32_t | | 4511 | static uint32_t |
4512 | iwm_mvm_scan_rxon_flags(struct iwm_softc *sc, int flags) | | 4512 | iwm_mvm_scan_rxon_flags(struct iwm_softc *sc, int flags) |
4513 | { | | 4513 | { |
4514 | if (flags & IEEE80211_CHAN_2GHZ) | | 4514 | if (flags & IEEE80211_CHAN_2GHZ) |
4515 | return htole32(IWM_PHY_BAND_24); | | 4515 | return htole32(IWM_PHY_BAND_24); |
4516 | else | | 4516 | else |
4517 | return htole32(IWM_PHY_BAND_5); | | 4517 | return htole32(IWM_PHY_BAND_5); |
4518 | } | | 4518 | } |
4519 | | | 4519 | |
4520 | static uint32_t | | 4520 | static uint32_t |
4521 | iwm_mvm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck) | | 4521 | iwm_mvm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck) |
4522 | { | | 4522 | { |
4523 | uint32_t tx_ant; | | 4523 | uint32_t tx_ant; |
4524 | int i, ind; | | 4524 | int i, ind; |
4525 | | | 4525 | |
4526 | for (i = 0, ind = sc->sc_scan_last_antenna; | | 4526 | for (i = 0, ind = sc->sc_scan_last_antenna; |
4527 | i < IWM_RATE_MCS_ANT_NUM; i++) { | | 4527 | i < IWM_RATE_MCS_ANT_NUM; i++) { |
4528 | ind = (ind + 1) % IWM_RATE_MCS_ANT_NUM; | | 4528 | ind = (ind + 1) % IWM_RATE_MCS_ANT_NUM; |
4529 | if (IWM_FW_VALID_TX_ANT(sc) & (1 << ind)) { | | 4529 | if (IWM_FW_VALID_TX_ANT(sc) & (1 << ind)) { |
4530 | sc->sc_scan_last_antenna = ind; | | 4530 | sc->sc_scan_last_antenna = ind; |
4531 | break; | | 4531 | break; |
4532 | } | | 4532 | } |
4533 | } | | 4533 | } |
4534 | tx_ant = (1 << sc->sc_scan_last_antenna) << IWM_RATE_MCS_ANT_POS; | | 4534 | tx_ant = (1 << sc->sc_scan_last_antenna) << IWM_RATE_MCS_ANT_POS; |
4535 | | | 4535 | |
4536 | if ((flags & IEEE80211_CHAN_2GHZ) && !no_cck) | | 4536 | if ((flags & IEEE80211_CHAN_2GHZ) && !no_cck) |
4537 | return htole32(IWM_RATE_1M_PLCP | IWM_RATE_MCS_CCK_MSK | | | 4537 | return htole32(IWM_RATE_1M_PLCP | IWM_RATE_MCS_CCK_MSK | |
4538 | tx_ant); | | 4538 | tx_ant); |
4539 | else | | 4539 | else |
4540 | return htole32(IWM_RATE_6M_PLCP | tx_ant); | | 4540 | return htole32(IWM_RATE_6M_PLCP | tx_ant); |
4541 | } | | 4541 | } |
4542 | | | 4542 | |
4543 | /* | | 4543 | /* |
4544 | * If req->n_ssids > 0, it means we should do an active scan. | | 4544 | * If req->n_ssids > 0, it means we should do an active scan. |
4545 | * In case of active scan w/o directed scan, we receive a zero-length SSID | | 4545 | * In case of active scan w/o directed scan, we receive a zero-length SSID |
4546 | * just to notify that this scan is active and not passive. | | 4546 | * just to notify that this scan is active and not passive. |
4547 | * In order to notify the FW of the number of SSIDs we wish to scan (including | | 4547 | * In order to notify the FW of the number of SSIDs we wish to scan (including |
4548 | * the zero-length one), we need to set the corresponding bits in chan->type, | | 4548 | * the zero-length one), we need to set the corresponding bits in chan->type, |
4549 | * one for each SSID, and set the active bit (first). If the first SSID is | | 4549 | * one for each SSID, and set the active bit (first). If the first SSID is |
4550 | * already included in the probe template, so we need to set only | | 4550 | * already included in the probe template, so we need to set only |
4551 | * req->n_ssids - 1 bits in addition to the first bit. | | 4551 | * req->n_ssids - 1 bits in addition to the first bit. |
4552 | */ | | 4552 | */ |
4553 | static uint16_t | | 4553 | static uint16_t |
4554 | iwm_mvm_get_active_dwell(struct iwm_softc *sc, int flags, int n_ssids) | | 4554 | iwm_mvm_get_active_dwell(struct iwm_softc *sc, int flags, int n_ssids) |
4555 | { | | 4555 | { |
4556 | if (flags & IEEE80211_CHAN_2GHZ) | | 4556 | if (flags & IEEE80211_CHAN_2GHZ) |
4557 | return 30 + 3 * (n_ssids + 1); | | 4557 | return 30 + 3 * (n_ssids + 1); |
4558 | return 20 + 2 * (n_ssids + 1); | | 4558 | return 20 + 2 * (n_ssids + 1); |
4559 | } | | 4559 | } |
4560 | | | 4560 | |
4561 | static uint16_t | | 4561 | static uint16_t |
4562 | iwm_mvm_get_passive_dwell(struct iwm_softc *sc, int flags) | | 4562 | iwm_mvm_get_passive_dwell(struct iwm_softc *sc, int flags) |
4563 | { | | 4563 | { |
4564 | return (flags & IEEE80211_CHAN_2GHZ) ? 100 + 20 : 100 + 10; | | 4564 | return (flags & IEEE80211_CHAN_2GHZ) ? 100 + 20 : 100 + 10; |
4565 | } | | 4565 | } |
4566 | | | 4566 | |
4567 | static int | | 4567 | static int |
4568 | iwm_mvm_scan_fill_channels(struct iwm_softc *sc, struct iwm_scan_cmd *cmd, | | 4568 | iwm_mvm_scan_fill_channels(struct iwm_softc *sc, struct iwm_scan_cmd *cmd, |
4569 | int flags, int n_ssids, int basic_ssid) | | 4569 | int flags, int n_ssids, int basic_ssid) |
4570 | { | | 4570 | { |
4571 | struct ieee80211com *ic = &sc->sc_ic; | | 4571 | struct ieee80211com *ic = &sc->sc_ic; |
4572 | uint16_t passive_dwell = iwm_mvm_get_passive_dwell(sc, flags); | | 4572 | uint16_t passive_dwell = iwm_mvm_get_passive_dwell(sc, flags); |
4573 | uint16_t active_dwell = iwm_mvm_get_active_dwell(sc, flags, n_ssids); | | 4573 | uint16_t active_dwell = iwm_mvm_get_active_dwell(sc, flags, n_ssids); |
4574 | struct iwm_scan_channel *chan = (struct iwm_scan_channel *) | | 4574 | struct iwm_scan_channel *chan = (struct iwm_scan_channel *) |
4575 | (cmd->data + le16toh(cmd->tx_cmd.len)); | | 4575 | (cmd->data + le16toh(cmd->tx_cmd.len)); |
4576 | int type = (1 << n_ssids) - 1; | | 4576 | int type = (1 << n_ssids) - 1; |
4577 | struct ieee80211_channel *c; | | 4577 | struct ieee80211_channel *c; |
4578 | int nchan; | | 4578 | int nchan; |
4579 | | | 4579 | |
4580 | if (!basic_ssid) | | 4580 | if (!basic_ssid) |
4581 | type |= (1 << n_ssids); | | 4581 | type |= (1 << n_ssids); |
4582 | | | 4582 | |
4583 | for (nchan = 0, c = &ic->ic_channels[1]; | | 4583 | for (nchan = 0, c = &ic->ic_channels[1]; |
4584 | c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; | | 4584 | c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; |
4585 | c++) { | | 4585 | c++) { |
4586 | if ((c->ic_flags & flags) != flags) | | 4586 | if ((c->ic_flags & flags) != flags) |
4587 | continue; | | 4587 | continue; |
4588 | | | 4588 | |
4589 | chan->channel = htole16(ieee80211_mhz2ieee(c->ic_freq, flags)); | | 4589 | chan->channel = htole16(ieee80211_mhz2ieee(c->ic_freq, flags)); |
4590 | chan->type = htole32(type); | | 4590 | chan->type = htole32(type); |
4591 | if (c->ic_flags & IEEE80211_CHAN_PASSIVE) | | 4591 | if (c->ic_flags & IEEE80211_CHAN_PASSIVE) |
4592 | chan->type &= htole32(~IWM_SCAN_CHANNEL_TYPE_ACTIVE); | | 4592 | chan->type &= htole32(~IWM_SCAN_CHANNEL_TYPE_ACTIVE); |
4593 | chan->active_dwell = htole16(active_dwell); | | 4593 | chan->active_dwell = htole16(active_dwell); |
4594 | chan->passive_dwell = htole16(passive_dwell); | | 4594 | chan->passive_dwell = htole16(passive_dwell); |
4595 | chan->iteration_count = htole16(1); | | 4595 | chan->iteration_count = htole16(1); |
4596 | chan++; | | 4596 | chan++; |
4597 | nchan++; | | 4597 | nchan++; |
4598 | } | | 4598 | } |
4599 | if (nchan == 0) | | 4599 | if (nchan == 0) |
4600 | DPRINTF(("%s: NO CHANNEL!\n", DEVNAME(sc))); | | 4600 | DPRINTF(("%s: NO CHANNEL!\n", DEVNAME(sc))); |
4601 | return nchan; | | 4601 | return nchan; |
4602 | } | | 4602 | } |
4603 | | | 4603 | |
4604 | /* | | 4604 | /* |
4605 | * Fill in probe request with the following parameters: | | 4605 | * Fill in probe request with the following parameters: |
4606 | * TA is our vif HW address, which mac80211 ensures we have. | | 4606 | * TA is our vif HW address, which mac80211 ensures we have. |
4607 | * Packet is broadcasted, so this is both SA and DA. | | 4607 | * Packet is broadcasted, so this is both SA and DA. |
4608 | * The probe request IE is made out of two: first comes the most prioritized | | 4608 | * The probe request IE is made out of two: first comes the most prioritized |
4609 | * SSID if a directed scan is requested. Second comes whatever extra | | 4609 | * SSID if a directed scan is requested. Second comes whatever extra |
4610 | * information was given to us as the scan request IE. | | 4610 | * information was given to us as the scan request IE. |
4611 | */ | | 4611 | */ |
4612 | static uint16_t | | 4612 | static uint16_t |
4613 | iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct ieee80211_frame *frame, | | 4613 | iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct ieee80211_frame *frame, |
4614 | const uint8_t *ta, int n_ssids, const uint8_t *ssid, int ssid_len, | | 4614 | const uint8_t *ta, int n_ssids, const uint8_t *ssid, int ssid_len, |
4615 | const uint8_t *ie, int ie_len, int left) | | 4615 | const uint8_t *ie, int ie_len, int left) |
4616 | { | | 4616 | { |
4617 | int len = 0; | | 4617 | int len = 0; |
4618 | uint8_t *pos = NULL; | | 4618 | uint8_t *pos = NULL; |
4619 | | | 4619 | |
4620 | /* Make sure there is enough space for the probe request, | | 4620 | /* Make sure there is enough space for the probe request, |
4621 | * two mandatory IEs and the data */ | | 4621 | * two mandatory IEs and the data */ |
4622 | left -= sizeof(*frame); | | 4622 | left -= sizeof(*frame); |
4623 | if (left < 0) | | 4623 | if (left < 0) |
4624 | return 0; | | 4624 | return 0; |
4625 | | | 4625 | |
4626 | frame->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | | | 4626 | frame->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | |
4627 | IEEE80211_FC0_SUBTYPE_PROBE_REQ; | | 4627 | IEEE80211_FC0_SUBTYPE_PROBE_REQ; |
4628 | frame->i_fc[1] = IEEE80211_FC1_DIR_NODS; | | 4628 | frame->i_fc[1] = IEEE80211_FC1_DIR_NODS; |
4629 | IEEE80211_ADDR_COPY(frame->i_addr1, etherbroadcastaddr); | | 4629 | IEEE80211_ADDR_COPY(frame->i_addr1, etherbroadcastaddr); |
4630 | memcpy(frame->i_addr2, ta, ETHER_ADDR_LEN); | | 4630 | memcpy(frame->i_addr2, ta, ETHER_ADDR_LEN); |
4631 | IEEE80211_ADDR_COPY(frame->i_addr3, etherbroadcastaddr); | | 4631 | IEEE80211_ADDR_COPY(frame->i_addr3, etherbroadcastaddr); |
4632 | | | 4632 | |
4633 | len += sizeof(*frame); | | 4633 | len += sizeof(*frame); |
4634 | CTASSERT(sizeof(*frame) == 24); | | 4634 | CTASSERT(sizeof(*frame) == 24); |
4635 | | | 4635 | |
4636 | /* for passive scans, no need to fill anything */ | | 4636 | /* for passive scans, no need to fill anything */ |
4637 | if (n_ssids == 0) | | 4637 | if (n_ssids == 0) |
4638 | return (uint16_t)len; | | 4638 | return (uint16_t)len; |
4639 | | | 4639 | |
4640 | /* points to the payload of the request */ | | 4640 | /* points to the payload of the request */ |
4641 | pos = (uint8_t *)frame + sizeof(*frame); | | 4641 | pos = (uint8_t *)frame + sizeof(*frame); |
4642 | | | 4642 | |
4643 | /* fill in our SSID IE */ | | 4643 | /* fill in our SSID IE */ |
4644 | left -= ssid_len + 2; | | 4644 | left -= ssid_len + 2; |
4645 | if (left < 0) | | 4645 | if (left < 0) |
4646 | return 0; | | 4646 | return 0; |
4647 | *pos++ = IEEE80211_ELEMID_SSID; | | 4647 | *pos++ = IEEE80211_ELEMID_SSID; |
4648 | *pos++ = ssid_len; | | 4648 | *pos++ = ssid_len; |
4649 | if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */ | | 4649 | if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */ |
4650 | memcpy(pos, ssid, ssid_len); | | 4650 | memcpy(pos, ssid, ssid_len); |
4651 | pos += ssid_len; | | 4651 | pos += ssid_len; |
4652 | } | | 4652 | } |
4653 | | | 4653 | |
4654 | len += ssid_len + 2; | | 4654 | len += ssid_len + 2; |
4655 | | | 4655 | |
4656 | if (left < ie_len) | | 4656 | if (left < ie_len) |
4657 | return len; | | 4657 | return len; |
4658 | | | 4658 | |
4659 | if (ie && ie_len) { | | 4659 | if (ie && ie_len) { |
4660 | memcpy(pos, ie, ie_len); | | 4660 | memcpy(pos, ie, ie_len); |
4661 | len += ie_len; | | 4661 | len += ie_len; |
4662 | } | | 4662 | } |
4663 | | | 4663 | |
4664 | return (uint16_t)len; | | 4664 | return (uint16_t)len; |
4665 | } | | 4665 | } |
4666 | | | 4666 | |
4667 | static int | | 4667 | static int |
4668 | iwm_mvm_scan_request(struct iwm_softc *sc, int flags, | | 4668 | iwm_mvm_scan_request(struct iwm_softc *sc, int flags, |
4669 | int n_ssids, uint8_t *ssid, int ssid_len) | | 4669 | int n_ssids, uint8_t *ssid, int ssid_len) |
4670 | { | | 4670 | { |
4671 | struct ieee80211com *ic = &sc->sc_ic; | | 4671 | struct ieee80211com *ic = &sc->sc_ic; |
4672 | struct iwm_host_cmd hcmd = { | | 4672 | struct iwm_host_cmd hcmd = { |
4673 | .id = IWM_SCAN_REQUEST_CMD, | | 4673 | .id = IWM_SCAN_REQUEST_CMD, |
4674 | .len = { 0, }, | | 4674 | .len = { 0, }, |
4675 | .data = { sc->sc_scan_cmd, }, | | 4675 | .data = { sc->sc_scan_cmd, }, |
4676 | .flags = IWM_CMD_SYNC, | | 4676 | .flags = IWM_CMD_SYNC, |
4677 | .dataflags = { IWM_HCMD_DFL_NOCOPY, }, | | 4677 | .dataflags = { IWM_HCMD_DFL_NOCOPY, }, |
4678 | }; | | 4678 | }; |
4679 | struct iwm_scan_cmd *cmd = sc->sc_scan_cmd; | | 4679 | struct iwm_scan_cmd *cmd = sc->sc_scan_cmd; |
4680 | int is_assoc = 0; | | 4680 | int is_assoc = 0; |
4681 | int ret; | | 4681 | int ret; |
4682 | uint32_t status; | | 4682 | uint32_t status; |
4683 | int basic_ssid = !(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_NO_BASIC_SSID); | | 4683 | int basic_ssid = !(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_NO_BASIC_SSID); |
4684 | | | 4684 | |
4685 | //lockdep_assert_held(&mvm->mutex); | | 4685 | //lockdep_assert_held(&mvm->mutex); |
4686 | | | 4686 | |
4687 | sc->sc_scanband = flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ); | | 4687 | sc->sc_scanband = flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ); |
4688 | | | 4688 | |
4689 | DPRINTF(("Handling ieee80211 scan request\n")); | | 4689 | DPRINTF(("Handling ieee80211 scan request\n")); |
4690 | memset(cmd, 0, sc->sc_scan_cmd_len); | | 4690 | memset(cmd, 0, sc->sc_scan_cmd_len); |
4691 | | | 4691 | |
4692 | cmd->quiet_time = htole16(IWM_ACTIVE_QUIET_TIME); | | 4692 | cmd->quiet_time = htole16(IWM_ACTIVE_QUIET_TIME); |
4693 | cmd->quiet_plcp_th = htole16(IWM_PLCP_QUIET_THRESH); | | 4693 | cmd->quiet_plcp_th = htole16(IWM_PLCP_QUIET_THRESH); |
4694 | cmd->rxchain_sel_flags = iwm_mvm_scan_rx_chain(sc); | | 4694 | cmd->rxchain_sel_flags = iwm_mvm_scan_rx_chain(sc); |
4695 | cmd->max_out_time = iwm_mvm_scan_max_out_time(sc, 0, is_assoc); | | 4695 | cmd->max_out_time = iwm_mvm_scan_max_out_time(sc, 0, is_assoc); |
4696 | cmd->suspend_time = iwm_mvm_scan_suspend_time(sc, is_assoc); | | 4696 | cmd->suspend_time = iwm_mvm_scan_suspend_time(sc, is_assoc); |
4697 | cmd->rxon_flags = iwm_mvm_scan_rxon_flags(sc, flags); | | 4697 | cmd->rxon_flags = iwm_mvm_scan_rxon_flags(sc, flags); |
4698 | cmd->filter_flags = htole32(IWM_MAC_FILTER_ACCEPT_GRP | | | 4698 | cmd->filter_flags = htole32(IWM_MAC_FILTER_ACCEPT_GRP | |
4699 | IWM_MAC_FILTER_IN_BEACON); | | 4699 | IWM_MAC_FILTER_IN_BEACON); |
4700 | | | 4700 | |
4701 | cmd->type = htole32(IWM_SCAN_TYPE_FORCED); | | 4701 | cmd->type = htole32(IWM_SCAN_TYPE_FORCED); |
4702 | cmd->repeats = htole32(1); | | 4702 | cmd->repeats = htole32(1); |
4703 | | | 4703 | |
4704 | /* | | 4704 | /* |
4705 | * If the user asked for passive scan, don't change to active scan if | | 4705 | * If the user asked for passive scan, don't change to active scan if |
4706 | * you see any activity on the channel - remain passive. | | 4706 | * you see any activity on the channel - remain passive. |
4707 | */ | | 4707 | */ |
4708 | if (n_ssids > 0) { | | 4708 | if (n_ssids > 0) { |
4709 | cmd->passive2active = htole16(1); | | 4709 | cmd->passive2active = htole16(1); |
4710 | cmd->scan_flags |= IWM_SCAN_FLAGS_PASSIVE2ACTIVE; | | 4710 | cmd->scan_flags |= IWM_SCAN_FLAGS_PASSIVE2ACTIVE; |
4711 | #if 0 | | 4711 | #if 0 |
4712 | if (basic_ssid) { | | 4712 | if (basic_ssid) { |
4713 | ssid = req->ssids[0].ssid; | | 4713 | ssid = req->ssids[0].ssid; |
4714 | ssid_len = req->ssids[0].ssid_len; | | 4714 | ssid_len = req->ssids[0].ssid_len; |
4715 | } | | 4715 | } |
4716 | #endif | | 4716 | #endif |
4717 | } else { | | 4717 | } else { |
4718 | cmd->passive2active = 0; | | 4718 | cmd->passive2active = 0; |
4719 | cmd->scan_flags &= ~IWM_SCAN_FLAGS_PASSIVE2ACTIVE; | | 4719 | cmd->scan_flags &= ~IWM_SCAN_FLAGS_PASSIVE2ACTIVE; |
4720 | } | | 4720 | } |
4721 | | | 4721 | |
4722 | cmd->tx_cmd.tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | | | 4722 | cmd->tx_cmd.tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | |
4723 | IWM_TX_CMD_FLG_BT_DIS); | | 4723 | IWM_TX_CMD_FLG_BT_DIS); |
4724 | cmd->tx_cmd.sta_id = sc->sc_aux_sta.sta_id; | | 4724 | cmd->tx_cmd.sta_id = sc->sc_aux_sta.sta_id; |
4725 | cmd->tx_cmd.life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); | | 4725 | cmd->tx_cmd.life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); |
4726 | cmd->tx_cmd.rate_n_flags = iwm_mvm_scan_rate_n_flags(sc, flags, 1/*XXX*/); | | 4726 | cmd->tx_cmd.rate_n_flags = iwm_mvm_scan_rate_n_flags(sc, flags, 1/*XXX*/); |
4727 | | | 4727 | |
4728 | cmd->tx_cmd.len = htole16(iwm_mvm_fill_probe_req(sc, | | 4728 | cmd->tx_cmd.len = htole16(iwm_mvm_fill_probe_req(sc, |
4729 | (struct ieee80211_frame *)cmd->data, | | 4729 | (struct ieee80211_frame *)cmd->data, |
4730 | ic->ic_myaddr, n_ssids, ssid, ssid_len, | | 4730 | ic->ic_myaddr, n_ssids, ssid, ssid_len, |
4731 | NULL, 0, sc->sc_capa_max_probe_len)); | | 4731 | NULL, 0, sc->sc_capa_max_probe_len)); |
4732 | | | 4732 | |
4733 | cmd->channel_count | | 4733 | cmd->channel_count |
4734 | = iwm_mvm_scan_fill_channels(sc, cmd, flags, n_ssids, basic_ssid); | | 4734 | = iwm_mvm_scan_fill_channels(sc, cmd, flags, n_ssids, basic_ssid); |
4735 | | | 4735 | |
4736 | cmd->len = htole16(sizeof(struct iwm_scan_cmd) + | | 4736 | cmd->len = htole16(sizeof(struct iwm_scan_cmd) + |
4737 | le16toh(cmd->tx_cmd.len) + | | 4737 | le16toh(cmd->tx_cmd.len) + |
4738 | (cmd->channel_count * sizeof(struct iwm_scan_channel))); | | 4738 | (cmd->channel_count * sizeof(struct iwm_scan_channel))); |
4739 | hcmd.len[0] = le16toh(cmd->len); | | 4739 | hcmd.len[0] = le16toh(cmd->len); |
4740 | | | 4740 | |
4741 | status = IWM_SCAN_RESPONSE_OK; | | 4741 | status = IWM_SCAN_RESPONSE_OK; |
4742 | ret = iwm_mvm_send_cmd_status(sc, &hcmd, &status); | | 4742 | ret = iwm_mvm_send_cmd_status(sc, &hcmd, &status); |
4743 | if (!ret && status == IWM_SCAN_RESPONSE_OK) { | | 4743 | if (!ret && status == IWM_SCAN_RESPONSE_OK) { |
4744 | DPRINTF(("Scan request was sent successfully\n")); | | 4744 | DPRINTF(("Scan request was sent successfully\n")); |
4745 | } else { | | 4745 | } else { |
4746 | /* | | 4746 | /* |
4747 | * If the scan failed, it usually means that the FW was unable | | 4747 | * If the scan failed, it usually means that the FW was unable |
4748 | * to allocate the time events. Warn on it, but maybe we | | 4748 | * to allocate the time events. Warn on it, but maybe we |
4749 | * should try to send the command again with different params. | | 4749 | * should try to send the command again with different params. |
4750 | */ | | 4750 | */ |
4751 | sc->sc_scanband = 0; | | 4751 | sc->sc_scanband = 0; |
4752 | ret = EIO; | | 4752 | ret = EIO; |
4753 | } | | 4753 | } |
4754 | return ret; | | 4754 | return ret; |
4755 | } | | 4755 | } |
4756 | | | 4756 | |
4757 | /* | | 4757 | /* |
4758 | * END mvm/scan.c | | 4758 | * END mvm/scan.c |
4759 | */ | | 4759 | */ |
4760 | | | 4760 | |
4761 | /* | | 4761 | /* |
4762 | * BEGIN mvm/mac-ctxt.c | | 4762 | * BEGIN mvm/mac-ctxt.c |
4763 | */ | | 4763 | */ |
4764 | | | 4764 | |
4765 | static void | | 4765 | static void |
4766 | iwm_mvm_ack_rates(struct iwm_softc *sc, struct iwm_node *in, | | 4766 | iwm_mvm_ack_rates(struct iwm_softc *sc, struct iwm_node *in, |
4767 | int *cck_rates, int *ofdm_rates) | | 4767 | int *cck_rates, int *ofdm_rates) |
4768 | { | | 4768 | { |
4769 | int lowest_present_ofdm = 100; | | 4769 | int lowest_present_ofdm = 100; |
4770 | int lowest_present_cck = 100; | | 4770 | int lowest_present_cck = 100; |
4771 | uint8_t cck = 0; | | 4771 | uint8_t cck = 0; |
4772 | uint8_t ofdm = 0; | | 4772 | uint8_t ofdm = 0; |
4773 | int i; | | 4773 | int i; |
4774 | | | 4774 | |
4775 | for (i = 0; i <= IWM_LAST_CCK_RATE; i++) { | | 4775 | for (i = 0; i <= IWM_LAST_CCK_RATE; i++) { |
4776 | cck |= (1 << i); | | 4776 | cck |= (1 << i); |
4777 | if (lowest_present_cck > i) | | 4777 | if (lowest_present_cck > i) |
4778 | lowest_present_cck = i; | | 4778 | lowest_present_cck = i; |
4779 | } | | 4779 | } |
4780 | for (i = IWM_FIRST_OFDM_RATE; i <= IWM_LAST_NON_HT_RATE; i++) { | | 4780 | for (i = IWM_FIRST_OFDM_RATE; i <= IWM_LAST_NON_HT_RATE; i++) { |
4781 | int adj = i - IWM_FIRST_OFDM_RATE; | | 4781 | int adj = i - IWM_FIRST_OFDM_RATE; |
4782 | ofdm |= (1 << adj); | | 4782 | ofdm |= (1 << adj); |
4783 | if (lowest_present_ofdm > i) | | 4783 | if (lowest_present_ofdm > i) |
4784 | lowest_present_ofdm = i; | | 4784 | lowest_present_ofdm = i; |
4785 | } | | 4785 | } |
4786 | | | 4786 | |
4787 | /* | | 4787 | /* |
4788 | * Now we've got the basic rates as bitmaps in the ofdm and cck | | 4788 | * Now we've got the basic rates as bitmaps in the ofdm and cck |
4789 | * variables. This isn't sufficient though, as there might not | | 4789 | * variables. This isn't sufficient though, as there might not |
4790 | * be all the right rates in the bitmap. E.g. if the only basic | | 4790 | * be all the right rates in the bitmap. E.g. if the only basic |
4791 | * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps | | 4791 | * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps |
4792 | * and 6 Mbps because the 802.11-2007 standard says in 9.6: | | 4792 | * and 6 Mbps because the 802.11-2007 standard says in 9.6: |
4793 | * | | 4793 | * |
4794 | * [...] a STA responding to a received frame shall transmit | | 4794 | * [...] a STA responding to a received frame shall transmit |
4795 | * its Control Response frame [...] at the highest rate in the | | 4795 | * its Control Response frame [...] at the highest rate in the |
4796 | * BSSBasicRateSet parameter that is less than or equal to the | | 4796 | * BSSBasicRateSet parameter that is less than or equal to the |
4797 | * rate of the immediately previous frame in the frame exchange | | 4797 | * rate of the immediately previous frame in the frame exchange |
4798 | * sequence ([...]) and that is of the same modulation class | | 4798 | * sequence ([...]) and that is of the same modulation class |
4799 | * ([...]) as the received frame. If no rate contained in the | | 4799 | * ([...]) as the received frame. If no rate contained in the |
4800 | * BSSBasicRateSet parameter meets these conditions, then the | | 4800 | * BSSBasicRateSet parameter meets these conditions, then the |
4801 | * control frame sent in response to a received frame shall be | | 4801 | * control frame sent in response to a received frame shall be |
4802 | * transmitted at the highest mandatory rate of the PHY that is | | 4802 | * transmitted at the highest mandatory rate of the PHY that is |
4803 | * less than or equal to the rate of the received frame, and | | 4803 | * less than or equal to the rate of the received frame, and |
4804 | * that is of the same modulation class as the received frame. | | 4804 | * that is of the same modulation class as the received frame. |
4805 | * | | 4805 | * |
4806 | * As a consequence, we need to add all mandatory rates that are | | 4806 | * As a consequence, we need to add all mandatory rates that are |
4807 | * lower than all of the basic rates to these bitmaps. | | 4807 | * lower than all of the basic rates to these bitmaps. |
4808 | */ | | 4808 | */ |
4809 | | | 4809 | |
4810 | if (IWM_RATE_24M_INDEX < lowest_present_ofdm) | | 4810 | if (IWM_RATE_24M_INDEX < lowest_present_ofdm) |
4811 | ofdm |= IWM_RATE_BIT_MSK(24) >> IWM_FIRST_OFDM_RATE; | | 4811 | ofdm |= IWM_RATE_BIT_MSK(24) >> IWM_FIRST_OFDM_RATE; |
4812 | if (IWM_RATE_12M_INDEX < lowest_present_ofdm) | | 4812 | if (IWM_RATE_12M_INDEX < lowest_present_ofdm) |
4813 | ofdm |= IWM_RATE_BIT_MSK(12) >> IWM_FIRST_OFDM_RATE; | | 4813 | ofdm |= IWM_RATE_BIT_MSK(12) >> IWM_FIRST_OFDM_RATE; |
4814 | /* 6M already there or needed so always add */ | | 4814 | /* 6M already there or needed so always add */ |
4815 | ofdm |= IWM_RATE_BIT_MSK(6) >> IWM_FIRST_OFDM_RATE; | | 4815 | ofdm |= IWM_RATE_BIT_MSK(6) >> IWM_FIRST_OFDM_RATE; |
4816 | | | 4816 | |
4817 | /* | | 4817 | /* |
4818 | * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. | | 4818 | * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. |
4819 | * Note, however: | | 4819 | * Note, however: |