| @@ -1,1108 +1,1108 @@ | | | @@ -1,1108 +1,1108 @@ |
1 | /* $NetBSD: if_iwm.c,v 1.78 2018/01/21 18:12:37 christos Exp $ */ | | 1 | /* $NetBSD: if_iwm.c,v 1.79 2018/06/05 12:17:18 knakahara Exp $ */ |
2 | /* OpenBSD: if_iwm.c,v 1.148 2016/11/19 21:07:08 stsp Exp */ | | 2 | /* OpenBSD: if_iwm.c,v 1.148 2016/11/19 21:07:08 stsp Exp */ |
3 | #define IEEE80211_NO_HT | | 3 | #define IEEE80211_NO_HT |
4 | /* | | 4 | /* |
5 | * Copyright (c) 2014, 2016 genua gmbh <info@genua.de> | | 5 | * Copyright (c) 2014, 2016 genua gmbh <info@genua.de> |
6 | * Author: Stefan Sperling <stsp@openbsd.org> | | 6 | * Author: Stefan Sperling <stsp@openbsd.org> |
7 | * Copyright (c) 2014 Fixup Software Ltd. | | 7 | * Copyright (c) 2014 Fixup Software Ltd. |
8 | * | | 8 | * |
9 | * Permission to use, copy, modify, and distribute this software for any | | 9 | * Permission to use, copy, modify, and distribute this software for any |
10 | * purpose with or without fee is hereby granted, provided that the above | | 10 | * purpose with or without fee is hereby granted, provided that the above |
11 | * copyright notice and this permission notice appear in all copies. | | 11 | * copyright notice and this permission notice appear in all copies. |
12 | * | | 12 | * |
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
19 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 19 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
20 | */ | | 20 | */ |
21 | | | 21 | |
22 | /*- | | 22 | /*- |
23 | * Based on BSD-licensed source modules in the Linux iwlwifi driver, | | 23 | * Based on BSD-licensed source modules in the Linux iwlwifi driver, |
24 | * which were used as the reference documentation for this implementation. | | 24 | * which were used as the reference documentation for this implementation. |
25 | * | | 25 | * |
26 | *********************************************************************** | | 26 | *********************************************************************** |
27 | * | | 27 | * |
28 | * This file is provided under a dual BSD/GPLv2 license. When using or | | 28 | * This file is provided under a dual BSD/GPLv2 license. When using or |
29 | * redistributing this file, you may do so under either license. | | 29 | * redistributing this file, you may do so under either license. |
30 | * | | 30 | * |
31 | * GPL LICENSE SUMMARY | | 31 | * GPL LICENSE SUMMARY |
32 | * | | 32 | * |
33 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. | | 33 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. |
34 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | | 34 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
35 | * Copyright(c) 2016 Intel Deutschland GmbH | | 35 | * Copyright(c) 2016 Intel Deutschland GmbH |
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 <linuxwifi@intel.com> | | 55 | * Intel Linux Wireless <linuxwifi@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 | * BSD LICENSE | | 58 | * BSD LICENSE |
59 | * | | 59 | * |
60 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. | | 60 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. |
61 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | | 61 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
62 | * Copyright(c) 2016 Intel Deutschland GmbH | | 62 | * Copyright(c) 2016 Intel Deutschland GmbH |
63 | * All rights reserved. | | 63 | * All rights reserved. |
64 | * | | 64 | * |
65 | * Redistribution and use in source and binary forms, with or without | | 65 | * Redistribution and use in source and binary forms, with or without |
66 | * modification, are permitted provided that the following conditions | | 66 | * modification, are permitted provided that the following conditions |
67 | * are met: | | 67 | * are met: |
68 | * | | 68 | * |
69 | * * Redistributions of source code must retain the above copyright | | 69 | * * Redistributions of source code must retain the above copyright |
70 | * notice, this list of conditions and the following disclaimer. | | 70 | * notice, this list of conditions and the following disclaimer. |
71 | * * Redistributions in binary form must reproduce the above copyright | | 71 | * * Redistributions in binary form must reproduce the above copyright |
72 | * notice, this list of conditions and the following disclaimer in | | 72 | * notice, this list of conditions and the following disclaimer in |
73 | * the documentation and/or other materials provided with the | | 73 | * the documentation and/or other materials provided with the |
74 | * distribution. | | 74 | * distribution. |
75 | * * Neither the name Intel Corporation nor the names of its | | 75 | * * Neither the name Intel Corporation nor the names of its |
76 | * contributors may be used to endorse or promote products derived | | 76 | * contributors may be used to endorse or promote products derived |
77 | * from this software without specific prior written permission. | | 77 | * from this software without specific prior written permission. |
78 | * | | 78 | * |
79 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | | 79 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
80 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | | 80 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
81 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | | 81 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
82 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | | 82 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
83 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | | 83 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
84 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | | 84 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
85 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | 85 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
86 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | 86 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
87 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | 87 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
88 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | | 88 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
89 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | 89 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
90 | */ | | 90 | */ |
91 | | | 91 | |
92 | /*- | | 92 | /*- |
93 | * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> | | 93 | * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> |
94 | * | | 94 | * |
95 | * Permission to use, copy, modify, and distribute this software for any | | 95 | * Permission to use, copy, modify, and distribute this software for any |
96 | * purpose with or without fee is hereby granted, provided that the above | | 96 | * purpose with or without fee is hereby granted, provided that the above |
97 | * copyright notice and this permission notice appear in all copies. | | 97 | * copyright notice and this permission notice appear in all copies. |
98 | * | | 98 | * |
99 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 99 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
100 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 100 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
101 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 101 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
102 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 102 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
103 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 103 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
104 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 104 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
105 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 105 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
106 | */ | | 106 | */ |
107 | | | 107 | |
108 | #include <sys/cdefs.h> | | 108 | #include <sys/cdefs.h> |
109 | __KERNEL_RCSID(0, "$NetBSD: if_iwm.c,v 1.78 2018/01/21 18:12:37 christos Exp $"); | | 109 | __KERNEL_RCSID(0, "$NetBSD: if_iwm.c,v 1.79 2018/06/05 12:17:18 knakahara Exp $"); |
110 | | | 110 | |
111 | #include <sys/param.h> | | 111 | #include <sys/param.h> |
112 | #include <sys/conf.h> | | 112 | #include <sys/conf.h> |
113 | #include <sys/kernel.h> | | 113 | #include <sys/kernel.h> |
114 | #include <sys/kmem.h> | | 114 | #include <sys/kmem.h> |
115 | #include <sys/mbuf.h> | | 115 | #include <sys/mbuf.h> |
116 | #include <sys/mutex.h> | | 116 | #include <sys/mutex.h> |
117 | #include <sys/proc.h> | | 117 | #include <sys/proc.h> |
118 | #include <sys/socket.h> | | 118 | #include <sys/socket.h> |
119 | #include <sys/sockio.h> | | 119 | #include <sys/sockio.h> |
120 | #include <sys/sysctl.h> | | 120 | #include <sys/sysctl.h> |
121 | #include <sys/systm.h> | | 121 | #include <sys/systm.h> |
122 | | | 122 | |
123 | #include <sys/cpu.h> | | 123 | #include <sys/cpu.h> |
124 | #include <sys/bus.h> | | 124 | #include <sys/bus.h> |
125 | #include <sys/workqueue.h> | | 125 | #include <sys/workqueue.h> |
126 | #include <machine/endian.h> | | 126 | #include <machine/endian.h> |
127 | #include <sys/intr.h> | | 127 | #include <sys/intr.h> |
128 | | | 128 | |
129 | #include <dev/pci/pcireg.h> | | 129 | #include <dev/pci/pcireg.h> |
130 | #include <dev/pci/pcivar.h> | | 130 | #include <dev/pci/pcivar.h> |
131 | #include <dev/pci/pcidevs.h> | | 131 | #include <dev/pci/pcidevs.h> |
132 | #include <dev/firmload.h> | | 132 | #include <dev/firmload.h> |
133 | | | 133 | |
134 | #include <net/bpf.h> | | 134 | #include <net/bpf.h> |
135 | #include <net/if.h> | | 135 | #include <net/if.h> |
136 | #include <net/if_dl.h> | | 136 | #include <net/if_dl.h> |
137 | #include <net/if_media.h> | | 137 | #include <net/if_media.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/ip.h> | | 141 | #include <netinet/ip.h> |
142 | | | 142 | |
143 | #include <net80211/ieee80211_var.h> | | 143 | #include <net80211/ieee80211_var.h> |
144 | #include <net80211/ieee80211_amrr.h> | | 144 | #include <net80211/ieee80211_amrr.h> |
145 | #include <net80211/ieee80211_radiotap.h> | | 145 | #include <net80211/ieee80211_radiotap.h> |
146 | | | 146 | |
147 | #define DEVNAME(_s) device_xname((_s)->sc_dev) | | 147 | #define DEVNAME(_s) device_xname((_s)->sc_dev) |
148 | #define IC2IFP(_ic_) ((_ic_)->ic_ifp) | | 148 | #define IC2IFP(_ic_) ((_ic_)->ic_ifp) |
149 | | | 149 | |
150 | #define le16_to_cpup(_a_) (le16toh(*(const uint16_t *)(_a_))) | | 150 | #define le16_to_cpup(_a_) (le16toh(*(const uint16_t *)(_a_))) |
151 | #define le32_to_cpup(_a_) (le32toh(*(const uint32_t *)(_a_))) | | 151 | #define le32_to_cpup(_a_) (le32toh(*(const uint32_t *)(_a_))) |
152 | | | 152 | |
153 | #ifdef IWM_DEBUG | | 153 | #ifdef IWM_DEBUG |
154 | #define DPRINTF(x) do { if (iwm_debug > 0) printf x; } while (0) | | 154 | #define DPRINTF(x) do { if (iwm_debug > 0) printf x; } while (0) |
155 | #define DPRINTFN(n, x) do { if (iwm_debug >= (n)) printf x; } while (0) | | 155 | #define DPRINTFN(n, x) do { if (iwm_debug >= (n)) printf x; } while (0) |
156 | int iwm_debug = 0; | | 156 | int iwm_debug = 0; |
157 | #else | | 157 | #else |
158 | #define DPRINTF(x) do { ; } while (0) | | 158 | #define DPRINTF(x) do { ; } while (0) |
159 | #define DPRINTFN(n, x) do { ; } while (0) | | 159 | #define DPRINTFN(n, x) do { ; } while (0) |
160 | #endif | | 160 | #endif |
161 | | | 161 | |
162 | #include <dev/pci/if_iwmreg.h> | | 162 | #include <dev/pci/if_iwmreg.h> |
163 | #include <dev/pci/if_iwmvar.h> | | 163 | #include <dev/pci/if_iwmvar.h> |
164 | | | 164 | |
165 | static const uint8_t iwm_nvm_channels[] = { | | 165 | static const uint8_t iwm_nvm_channels[] = { |
166 | /* 2.4 GHz */ | | 166 | /* 2.4 GHz */ |
167 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | | 167 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
168 | /* 5 GHz */ | | 168 | /* 5 GHz */ |
169 | 36, 40, 44, 48, 52, 56, 60, 64, | | 169 | 36, 40, 44, 48, 52, 56, 60, 64, |
170 | 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, | | 170 | 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, |
171 | 149, 153, 157, 161, 165 | | 171 | 149, 153, 157, 161, 165 |
172 | }; | | 172 | }; |
173 | | | 173 | |
174 | static const uint8_t iwm_nvm_channels_8000[] = { | | 174 | static const uint8_t iwm_nvm_channels_8000[] = { |
175 | /* 2.4 GHz */ | | 175 | /* 2.4 GHz */ |
176 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | | 176 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
177 | /* 5 GHz */ | | 177 | /* 5 GHz */ |
178 | 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, | | 178 | 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, |
179 | 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, | | 179 | 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, |
180 | 149, 153, 157, 161, 165, 169, 173, 177, 181 | | 180 | 149, 153, 157, 161, 165, 169, 173, 177, 181 |
181 | }; | | 181 | }; |
182 | | | 182 | |
183 | #define IWM_NUM_2GHZ_CHANNELS 14 | | 183 | #define IWM_NUM_2GHZ_CHANNELS 14 |
184 | | | 184 | |
185 | static const struct iwm_rate { | | 185 | static const struct iwm_rate { |
186 | uint8_t rate; | | 186 | uint8_t rate; |
187 | uint8_t plcp; | | 187 | uint8_t plcp; |
188 | uint8_t ht_plcp; | | 188 | uint8_t ht_plcp; |
189 | } iwm_rates[] = { | | 189 | } iwm_rates[] = { |
190 | /* Legacy */ /* HT */ | | 190 | /* Legacy */ /* HT */ |
191 | { 2, IWM_RATE_1M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, | | 191 | { 2, IWM_RATE_1M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, |
192 | { 4, IWM_RATE_2M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, | | 192 | { 4, IWM_RATE_2M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, |
193 | { 11, IWM_RATE_5M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, | | 193 | { 11, IWM_RATE_5M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, |
194 | { 22, IWM_RATE_11M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, | | 194 | { 22, IWM_RATE_11M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, |
195 | { 12, IWM_RATE_6M_PLCP, IWM_RATE_HT_SISO_MCS_0_PLCP }, | | 195 | { 12, IWM_RATE_6M_PLCP, IWM_RATE_HT_SISO_MCS_0_PLCP }, |
196 | { 18, IWM_RATE_9M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, | | 196 | { 18, IWM_RATE_9M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, |
197 | { 24, IWM_RATE_12M_PLCP, IWM_RATE_HT_SISO_MCS_1_PLCP }, | | 197 | { 24, IWM_RATE_12M_PLCP, IWM_RATE_HT_SISO_MCS_1_PLCP }, |
198 | { 36, IWM_RATE_18M_PLCP, IWM_RATE_HT_SISO_MCS_2_PLCP }, | | 198 | { 36, IWM_RATE_18M_PLCP, IWM_RATE_HT_SISO_MCS_2_PLCP }, |
199 | { 48, IWM_RATE_24M_PLCP, IWM_RATE_HT_SISO_MCS_3_PLCP }, | | 199 | { 48, IWM_RATE_24M_PLCP, IWM_RATE_HT_SISO_MCS_3_PLCP }, |
200 | { 72, IWM_RATE_36M_PLCP, IWM_RATE_HT_SISO_MCS_4_PLCP }, | | 200 | { 72, IWM_RATE_36M_PLCP, IWM_RATE_HT_SISO_MCS_4_PLCP }, |
201 | { 96, IWM_RATE_48M_PLCP, IWM_RATE_HT_SISO_MCS_5_PLCP }, | | 201 | { 96, IWM_RATE_48M_PLCP, IWM_RATE_HT_SISO_MCS_5_PLCP }, |
202 | { 108, IWM_RATE_54M_PLCP, IWM_RATE_HT_SISO_MCS_6_PLCP }, | | 202 | { 108, IWM_RATE_54M_PLCP, IWM_RATE_HT_SISO_MCS_6_PLCP }, |
203 | { 128, IWM_RATE_INVM_PLCP, IWM_RATE_HT_SISO_MCS_7_PLCP }, | | 203 | { 128, IWM_RATE_INVM_PLCP, IWM_RATE_HT_SISO_MCS_7_PLCP }, |
204 | }; | | 204 | }; |
205 | #define IWM_RIDX_CCK 0 | | 205 | #define IWM_RIDX_CCK 0 |
206 | #define IWM_RIDX_OFDM 4 | | 206 | #define IWM_RIDX_OFDM 4 |
207 | #define IWM_RIDX_MAX (__arraycount(iwm_rates)-1) | | 207 | #define IWM_RIDX_MAX (__arraycount(iwm_rates)-1) |
208 | #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) | | 208 | #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) |
209 | #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) | | 209 | #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) |
210 | | | 210 | |
211 | #ifndef IEEE80211_NO_HT | | 211 | #ifndef IEEE80211_NO_HT |
212 | /* Convert an MCS index into an iwm_rates[] index. */ | | 212 | /* Convert an MCS index into an iwm_rates[] index. */ |
213 | static const int iwm_mcs2ridx[] = { | | 213 | static const int iwm_mcs2ridx[] = { |
214 | IWM_RATE_MCS_0_INDEX, | | 214 | IWM_RATE_MCS_0_INDEX, |
215 | IWM_RATE_MCS_1_INDEX, | | 215 | IWM_RATE_MCS_1_INDEX, |
216 | IWM_RATE_MCS_2_INDEX, | | 216 | IWM_RATE_MCS_2_INDEX, |
217 | IWM_RATE_MCS_3_INDEX, | | 217 | IWM_RATE_MCS_3_INDEX, |
218 | IWM_RATE_MCS_4_INDEX, | | 218 | IWM_RATE_MCS_4_INDEX, |
219 | IWM_RATE_MCS_5_INDEX, | | 219 | IWM_RATE_MCS_5_INDEX, |
220 | IWM_RATE_MCS_6_INDEX, | | 220 | IWM_RATE_MCS_6_INDEX, |
221 | IWM_RATE_MCS_7_INDEX, | | 221 | IWM_RATE_MCS_7_INDEX, |
222 | }; | | 222 | }; |
223 | #endif | | 223 | #endif |
224 | | | 224 | |
225 | struct iwm_nvm_section { | | 225 | struct iwm_nvm_section { |
226 | uint16_t length; | | 226 | uint16_t length; |
227 | uint8_t *data; | | 227 | uint8_t *data; |
228 | }; | | 228 | }; |
229 | | | 229 | |
230 | struct iwm_newstate_state { | | 230 | struct iwm_newstate_state { |
231 | struct work ns_wk; | | 231 | struct work ns_wk; |
232 | enum ieee80211_state ns_nstate; | | 232 | enum ieee80211_state ns_nstate; |
233 | int ns_arg; | | 233 | int ns_arg; |
234 | int ns_generation; | | 234 | int ns_generation; |
235 | }; | | 235 | }; |
236 | | | 236 | |
237 | static int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t); | | 237 | static int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t); |
238 | static int iwm_firmware_store_section(struct iwm_softc *, | | 238 | static int iwm_firmware_store_section(struct iwm_softc *, |
239 | enum iwm_ucode_type, uint8_t *, size_t); | | 239 | enum iwm_ucode_type, uint8_t *, size_t); |
240 | static int iwm_set_default_calib(struct iwm_softc *, const void *); | | 240 | static int iwm_set_default_calib(struct iwm_softc *, const void *); |
241 | static int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); | | 241 | static int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); |
242 | static uint32_t iwm_read_prph(struct iwm_softc *, uint32_t); | | 242 | static uint32_t iwm_read_prph(struct iwm_softc *, uint32_t); |
243 | static void iwm_write_prph(struct iwm_softc *, uint32_t, uint32_t); | | 243 | static void iwm_write_prph(struct iwm_softc *, uint32_t, uint32_t); |
244 | #ifdef IWM_DEBUG | | 244 | #ifdef IWM_DEBUG |
245 | static int iwm_read_mem(struct iwm_softc *, uint32_t, void *, int); | | 245 | static int iwm_read_mem(struct iwm_softc *, uint32_t, void *, int); |
246 | #endif | | 246 | #endif |
247 | static int iwm_write_mem(struct iwm_softc *, uint32_t, const void *, int); | | 247 | static int iwm_write_mem(struct iwm_softc *, uint32_t, const void *, int); |
248 | static int iwm_write_mem32(struct iwm_softc *, uint32_t, uint32_t); | | 248 | static int iwm_write_mem32(struct iwm_softc *, uint32_t, uint32_t); |
249 | static int iwm_poll_bit(struct iwm_softc *, int, uint32_t, uint32_t, int); | | 249 | static int iwm_poll_bit(struct iwm_softc *, int, uint32_t, uint32_t, int); |
250 | static int iwm_nic_lock(struct iwm_softc *); | | 250 | static int iwm_nic_lock(struct iwm_softc *); |
251 | static void iwm_nic_unlock(struct iwm_softc *); | | 251 | static void iwm_nic_unlock(struct iwm_softc *); |
252 | static void iwm_set_bits_mask_prph(struct iwm_softc *, uint32_t, uint32_t, | | 252 | static void iwm_set_bits_mask_prph(struct iwm_softc *, uint32_t, uint32_t, |
253 | uint32_t); | | 253 | uint32_t); |
254 | static void iwm_set_bits_prph(struct iwm_softc *, uint32_t, uint32_t); | | 254 | static void iwm_set_bits_prph(struct iwm_softc *, uint32_t, uint32_t); |
255 | static void iwm_clear_bits_prph(struct iwm_softc *, uint32_t, uint32_t); | | 255 | static void iwm_clear_bits_prph(struct iwm_softc *, uint32_t, uint32_t); |
256 | static int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, | | 256 | static int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, |
257 | bus_size_t, bus_size_t); | | 257 | bus_size_t, bus_size_t); |
258 | static void iwm_dma_contig_free(struct iwm_dma_info *); | | 258 | static void iwm_dma_contig_free(struct iwm_dma_info *); |
259 | static int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); | | 259 | static int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); |
260 | static void iwm_disable_rx_dma(struct iwm_softc *); | | 260 | static void iwm_disable_rx_dma(struct iwm_softc *); |
261 | static void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); | | 261 | static void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); |
262 | static void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); | | 262 | static void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); |
263 | static int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, | | 263 | static int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, |
264 | int); | | 264 | int); |
265 | static void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); | | 265 | static void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); |
266 | static void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); | | 266 | static void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); |
267 | static void iwm_enable_rfkill_int(struct iwm_softc *); | | 267 | static void iwm_enable_rfkill_int(struct iwm_softc *); |
268 | static int iwm_check_rfkill(struct iwm_softc *); | | 268 | static int iwm_check_rfkill(struct iwm_softc *); |
269 | static void iwm_enable_interrupts(struct iwm_softc *); | | 269 | static void iwm_enable_interrupts(struct iwm_softc *); |
270 | static void iwm_restore_interrupts(struct iwm_softc *); | | 270 | static void iwm_restore_interrupts(struct iwm_softc *); |
271 | static void iwm_disable_interrupts(struct iwm_softc *); | | 271 | static void iwm_disable_interrupts(struct iwm_softc *); |
272 | static void iwm_ict_reset(struct iwm_softc *); | | 272 | static void iwm_ict_reset(struct iwm_softc *); |
273 | static int iwm_set_hw_ready(struct iwm_softc *); | | 273 | static int iwm_set_hw_ready(struct iwm_softc *); |
274 | static int iwm_prepare_card_hw(struct iwm_softc *); | | 274 | static int iwm_prepare_card_hw(struct iwm_softc *); |
275 | static void iwm_apm_config(struct iwm_softc *); | | 275 | static void iwm_apm_config(struct iwm_softc *); |
276 | static int iwm_apm_init(struct iwm_softc *); | | 276 | static int iwm_apm_init(struct iwm_softc *); |
277 | static void iwm_apm_stop(struct iwm_softc *); | | 277 | static void iwm_apm_stop(struct iwm_softc *); |
278 | static int iwm_allow_mcast(struct iwm_softc *); | | 278 | static int iwm_allow_mcast(struct iwm_softc *); |
279 | static int iwm_start_hw(struct iwm_softc *); | | 279 | static int iwm_start_hw(struct iwm_softc *); |
280 | static void iwm_stop_device(struct iwm_softc *); | | 280 | static void iwm_stop_device(struct iwm_softc *); |
281 | static void iwm_nic_config(struct iwm_softc *); | | 281 | static void iwm_nic_config(struct iwm_softc *); |
282 | static int iwm_nic_rx_init(struct iwm_softc *); | | 282 | static int iwm_nic_rx_init(struct iwm_softc *); |
283 | static int iwm_nic_tx_init(struct iwm_softc *); | | 283 | static int iwm_nic_tx_init(struct iwm_softc *); |
284 | static int iwm_nic_init(struct iwm_softc *); | | 284 | static int iwm_nic_init(struct iwm_softc *); |
285 | static int iwm_enable_txq(struct iwm_softc *, int, int, int); | | 285 | static int iwm_enable_txq(struct iwm_softc *, int, int, int); |
286 | static int iwm_post_alive(struct iwm_softc *); | | 286 | static int iwm_post_alive(struct iwm_softc *); |
287 | static struct iwm_phy_db_entry * | | 287 | static struct iwm_phy_db_entry * |
288 | iwm_phy_db_get_section(struct iwm_softc *, | | 288 | iwm_phy_db_get_section(struct iwm_softc *, |
289 | enum iwm_phy_db_section_type, uint16_t); | | 289 | enum iwm_phy_db_section_type, uint16_t); |
290 | static int iwm_phy_db_set_section(struct iwm_softc *, | | 290 | static int iwm_phy_db_set_section(struct iwm_softc *, |
291 | struct iwm_calib_res_notif_phy_db *, uint16_t); | | 291 | struct iwm_calib_res_notif_phy_db *, uint16_t); |
292 | static int iwm_is_valid_channel(uint16_t); | | 292 | static int iwm_is_valid_channel(uint16_t); |
293 | static uint8_t iwm_ch_id_to_ch_index(uint16_t); | | 293 | static uint8_t iwm_ch_id_to_ch_index(uint16_t); |
294 | static uint16_t iwm_channel_id_to_papd(uint16_t); | | 294 | static uint16_t iwm_channel_id_to_papd(uint16_t); |
295 | static uint16_t iwm_channel_id_to_txp(struct iwm_softc *, uint16_t); | | 295 | static uint16_t iwm_channel_id_to_txp(struct iwm_softc *, uint16_t); |
296 | static int iwm_phy_db_get_section_data(struct iwm_softc *, uint32_t, | | 296 | static int iwm_phy_db_get_section_data(struct iwm_softc *, uint32_t, |
297 | uint8_t **, uint16_t *, uint16_t); | | 297 | uint8_t **, uint16_t *, uint16_t); |
298 | static int iwm_send_phy_db_cmd(struct iwm_softc *, uint16_t, uint16_t, | | 298 | static int iwm_send_phy_db_cmd(struct iwm_softc *, uint16_t, uint16_t, |
299 | void *); | | 299 | void *); |
300 | static int iwm_phy_db_send_all_channel_groups(struct iwm_softc *, | | 300 | static int iwm_phy_db_send_all_channel_groups(struct iwm_softc *, |
301 | enum iwm_phy_db_section_type, uint8_t); | | 301 | enum iwm_phy_db_section_type, uint8_t); |
302 | static int iwm_send_phy_db_data(struct iwm_softc *); | | 302 | static int iwm_send_phy_db_data(struct iwm_softc *); |
303 | static void iwm_te_v2_to_v1(const struct iwm_time_event_cmd_v2 *, | | 303 | static void iwm_te_v2_to_v1(const struct iwm_time_event_cmd_v2 *, |
304 | struct iwm_time_event_cmd_v1 *); | | 304 | struct iwm_time_event_cmd_v1 *); |
305 | static int iwm_send_time_event_cmd(struct iwm_softc *, | | 305 | static int iwm_send_time_event_cmd(struct iwm_softc *, |
306 | const struct iwm_time_event_cmd_v2 *); | | 306 | const struct iwm_time_event_cmd_v2 *); |
307 | static void iwm_protect_session(struct iwm_softc *, struct iwm_node *, | | 307 | static void iwm_protect_session(struct iwm_softc *, struct iwm_node *, |
308 | uint32_t, uint32_t); | | 308 | uint32_t, uint32_t); |
309 | static int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, | | 309 | static int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, |
310 | uint16_t, uint8_t *, uint16_t *); | | 310 | uint16_t, uint8_t *, uint16_t *); |
311 | static int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, | | 311 | static int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, |
312 | uint16_t *, size_t); | | 312 | uint16_t *, size_t); |
313 | static void iwm_init_channel_map(struct iwm_softc *, const uint16_t * const, | | 313 | static void iwm_init_channel_map(struct iwm_softc *, const uint16_t * const, |
314 | const uint8_t *, size_t); | | 314 | const uint8_t *, size_t); |
315 | #ifndef IEEE80211_NO_HT | | 315 | #ifndef IEEE80211_NO_HT |
316 | static void iwm_setup_ht_rates(struct iwm_softc *); | | 316 | static void iwm_setup_ht_rates(struct iwm_softc *); |
317 | static void iwm_htprot_task(void *); | | 317 | static void iwm_htprot_task(void *); |
318 | static void iwm_update_htprot(struct ieee80211com *, | | 318 | static void iwm_update_htprot(struct ieee80211com *, |
319 | struct ieee80211_node *); | | 319 | struct ieee80211_node *); |
320 | static int iwm_ampdu_rx_start(struct ieee80211com *, | | 320 | static int iwm_ampdu_rx_start(struct ieee80211com *, |
321 | struct ieee80211_node *, uint8_t); | | 321 | struct ieee80211_node *, uint8_t); |
322 | static void iwm_ampdu_rx_stop(struct ieee80211com *, | | 322 | static void iwm_ampdu_rx_stop(struct ieee80211com *, |
323 | struct ieee80211_node *, uint8_t); | | 323 | struct ieee80211_node *, uint8_t); |
324 | static void iwm_sta_rx_agg(struct iwm_softc *, struct ieee80211_node *, | | 324 | static void iwm_sta_rx_agg(struct iwm_softc *, struct ieee80211_node *, |
325 | uint8_t, uint16_t, int); | | 325 | uint8_t, uint16_t, int); |
326 | #ifdef notyet | | 326 | #ifdef notyet |
327 | static int iwm_ampdu_tx_start(struct ieee80211com *, | | 327 | static int iwm_ampdu_tx_start(struct ieee80211com *, |
328 | struct ieee80211_node *, uint8_t); | | 328 | struct ieee80211_node *, uint8_t); |
329 | static void iwm_ampdu_tx_stop(struct ieee80211com *, | | 329 | static void iwm_ampdu_tx_stop(struct ieee80211com *, |
330 | struct ieee80211_node *, uint8_t); | | 330 | struct ieee80211_node *, uint8_t); |
331 | #endif | | 331 | #endif |
332 | static void iwm_ba_task(void *); | | 332 | static void iwm_ba_task(void *); |
333 | #endif | | 333 | #endif |
334 | | | 334 | |
335 | static int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, | | 335 | static int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, |
336 | const uint16_t *, const uint16_t *, const uint16_t *, | | 336 | const uint16_t *, const uint16_t *, const uint16_t *, |
337 | const uint16_t *, const uint16_t *); | | 337 | const uint16_t *, const uint16_t *); |
338 | static void iwm_set_hw_address_8000(struct iwm_softc *, | | 338 | static void iwm_set_hw_address_8000(struct iwm_softc *, |
339 | struct iwm_nvm_data *, const uint16_t *, const uint16_t *); | | 339 | struct iwm_nvm_data *, const uint16_t *, const uint16_t *); |
340 | static int iwm_parse_nvm_sections(struct iwm_softc *, | | 340 | static int iwm_parse_nvm_sections(struct iwm_softc *, |
341 | struct iwm_nvm_section *); | | 341 | struct iwm_nvm_section *); |
342 | static int iwm_nvm_init(struct iwm_softc *); | | 342 | static int iwm_nvm_init(struct iwm_softc *); |
343 | static int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, | | 343 | static int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, |
344 | const uint8_t *, uint32_t); | | 344 | const uint8_t *, uint32_t); |
345 | static int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, | | 345 | static int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, |
346 | const uint8_t *, uint32_t); | | 346 | const uint8_t *, uint32_t); |
347 | static int iwm_load_cpu_sections_7000(struct iwm_softc *, | | 347 | static int iwm_load_cpu_sections_7000(struct iwm_softc *, |
348 | struct iwm_fw_sects *, int , int *); | | 348 | struct iwm_fw_sects *, int , int *); |
349 | static int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); | | 349 | static int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); |
350 | static int iwm_load_cpu_sections_8000(struct iwm_softc *, | | 350 | static int iwm_load_cpu_sections_8000(struct iwm_softc *, |
351 | struct iwm_fw_sects *, int , int *); | | 351 | struct iwm_fw_sects *, int , int *); |
352 | static int iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type); | | 352 | static int iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type); |
353 | static int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); | | 353 | static int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); |
354 | static int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); | | 354 | static int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); |
355 | static int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); | | 355 | static int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); |
356 | static int iwm_send_phy_cfg_cmd(struct iwm_softc *); | | 356 | static int iwm_send_phy_cfg_cmd(struct iwm_softc *); |
357 | static int iwm_load_ucode_wait_alive(struct iwm_softc *, | | 357 | static int iwm_load_ucode_wait_alive(struct iwm_softc *, |
358 | enum iwm_ucode_type); | | 358 | enum iwm_ucode_type); |
359 | static int iwm_run_init_mvm_ucode(struct iwm_softc *, int); | | 359 | static int iwm_run_init_mvm_ucode(struct iwm_softc *, int); |
360 | static int iwm_rx_addbuf(struct iwm_softc *, int, int); | | 360 | static int iwm_rx_addbuf(struct iwm_softc *, int, int); |
361 | static int iwm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); | | 361 | static int iwm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); |
362 | static int iwm_get_signal_strength(struct iwm_softc *, | | 362 | static int iwm_get_signal_strength(struct iwm_softc *, |
363 | struct iwm_rx_phy_info *); | | 363 | struct iwm_rx_phy_info *); |
364 | static void iwm_rx_rx_phy_cmd(struct iwm_softc *, | | 364 | static void iwm_rx_rx_phy_cmd(struct iwm_softc *, |
365 | struct iwm_rx_packet *, struct iwm_rx_data *); | | 365 | struct iwm_rx_packet *, struct iwm_rx_data *); |
366 | static int iwm_get_noise(const struct iwm_statistics_rx_non_phy *); | | 366 | static int iwm_get_noise(const struct iwm_statistics_rx_non_phy *); |
367 | static void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, | | 367 | static void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, |
368 | struct iwm_rx_data *); | | 368 | struct iwm_rx_data *); |
369 | static void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_node *); | | 369 | static void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_node *); |
370 | static void iwm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, | | 370 | static void iwm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, |
371 | struct iwm_rx_data *); | | 371 | struct iwm_rx_data *); |
372 | static int iwm_binding_cmd(struct iwm_softc *, struct iwm_node *, | | 372 | static int iwm_binding_cmd(struct iwm_softc *, struct iwm_node *, |
373 | uint32_t); | | 373 | uint32_t); |
374 | #if 0 | | 374 | #if 0 |
375 | static int iwm_binding_update(struct iwm_softc *, struct iwm_node *, int); | | 375 | static int iwm_binding_update(struct iwm_softc *, struct iwm_node *, int); |
376 | static int iwm_binding_add_vif(struct iwm_softc *, struct iwm_node *); | | 376 | static int iwm_binding_add_vif(struct iwm_softc *, struct iwm_node *); |
377 | #endif | | 377 | #endif |
378 | static void iwm_phy_ctxt_cmd_hdr(struct iwm_softc *, struct iwm_phy_ctxt *, | | 378 | static void iwm_phy_ctxt_cmd_hdr(struct iwm_softc *, struct iwm_phy_ctxt *, |
379 | struct iwm_phy_context_cmd *, uint32_t, uint32_t); | | 379 | struct iwm_phy_context_cmd *, uint32_t, uint32_t); |
380 | static void iwm_phy_ctxt_cmd_data(struct iwm_softc *, | | 380 | static void iwm_phy_ctxt_cmd_data(struct iwm_softc *, |
381 | struct iwm_phy_context_cmd *, struct ieee80211_channel *, | | 381 | struct iwm_phy_context_cmd *, struct ieee80211_channel *, |
382 | uint8_t, uint8_t); | | 382 | uint8_t, uint8_t); |
383 | static int iwm_phy_ctxt_cmd(struct iwm_softc *, struct iwm_phy_ctxt *, | | 383 | static int iwm_phy_ctxt_cmd(struct iwm_softc *, struct iwm_phy_ctxt *, |
384 | uint8_t, uint8_t, uint32_t, uint32_t); | | 384 | uint8_t, uint8_t, uint32_t, uint32_t); |
385 | static int iwm_send_cmd(struct iwm_softc *, struct iwm_host_cmd *); | | 385 | static int iwm_send_cmd(struct iwm_softc *, struct iwm_host_cmd *); |
386 | static int iwm_send_cmd_pdu(struct iwm_softc *, uint32_t, uint32_t, | | 386 | static int iwm_send_cmd_pdu(struct iwm_softc *, uint32_t, uint32_t, |
387 | uint16_t, const void *); | | 387 | uint16_t, const void *); |
388 | static int iwm_send_cmd_status(struct iwm_softc *, struct iwm_host_cmd *, | | 388 | static int iwm_send_cmd_status(struct iwm_softc *, struct iwm_host_cmd *, |
389 | uint32_t *); | | 389 | uint32_t *); |
390 | static int iwm_send_cmd_pdu_status(struct iwm_softc *, uint32_t, uint16_t, | | 390 | static int iwm_send_cmd_pdu_status(struct iwm_softc *, uint32_t, uint16_t, |
391 | const void *, uint32_t *); | | 391 | const void *, uint32_t *); |
392 | static void iwm_free_resp(struct iwm_softc *, struct iwm_host_cmd *); | | 392 | static void iwm_free_resp(struct iwm_softc *, struct iwm_host_cmd *); |
393 | static void iwm_cmd_done(struct iwm_softc *, int qid, int idx); | | 393 | static void iwm_cmd_done(struct iwm_softc *, int qid, int idx); |
394 | #if 0 | | 394 | #if 0 |
395 | static void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, | | 395 | static void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, |
396 | uint16_t); | | 396 | uint16_t); |
397 | #endif | | 397 | #endif |
398 | static const struct iwm_rate * | | 398 | static const struct iwm_rate * |
399 | iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *, | | 399 | iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *, |
400 | struct ieee80211_frame *, struct iwm_tx_cmd *); | | 400 | struct ieee80211_frame *, struct iwm_tx_cmd *); |
401 | static int iwm_tx(struct iwm_softc *, struct mbuf *, | | 401 | static int iwm_tx(struct iwm_softc *, struct mbuf *, |
402 | struct ieee80211_node *, int); | | 402 | struct ieee80211_node *, int); |
403 | static void iwm_led_enable(struct iwm_softc *); | | 403 | static void iwm_led_enable(struct iwm_softc *); |
404 | static void iwm_led_disable(struct iwm_softc *); | | 404 | static void iwm_led_disable(struct iwm_softc *); |
405 | static int iwm_led_is_enabled(struct iwm_softc *); | | 405 | static int iwm_led_is_enabled(struct iwm_softc *); |
406 | static void iwm_led_blink_timeout(void *); | | 406 | static void iwm_led_blink_timeout(void *); |
407 | static void iwm_led_blink_start(struct iwm_softc *); | | 407 | static void iwm_led_blink_start(struct iwm_softc *); |
408 | static void iwm_led_blink_stop(struct iwm_softc *); | | 408 | static void iwm_led_blink_stop(struct iwm_softc *); |
409 | static int iwm_beacon_filter_send_cmd(struct iwm_softc *, | | 409 | static int iwm_beacon_filter_send_cmd(struct iwm_softc *, |
410 | struct iwm_beacon_filter_cmd *); | | 410 | struct iwm_beacon_filter_cmd *); |
411 | static void iwm_beacon_filter_set_cqm_params(struct iwm_softc *, | | 411 | static void iwm_beacon_filter_set_cqm_params(struct iwm_softc *, |
412 | struct iwm_node *, struct iwm_beacon_filter_cmd *); | | 412 | struct iwm_node *, struct iwm_beacon_filter_cmd *); |
413 | static int iwm_update_beacon_abort(struct iwm_softc *, struct iwm_node *, | | 413 | static int iwm_update_beacon_abort(struct iwm_softc *, struct iwm_node *, |
414 | int); | | 414 | int); |
415 | static void iwm_power_build_cmd(struct iwm_softc *, struct iwm_node *, | | 415 | static void iwm_power_build_cmd(struct iwm_softc *, struct iwm_node *, |
416 | struct iwm_mac_power_cmd *); | | 416 | struct iwm_mac_power_cmd *); |
417 | static int iwm_power_mac_update_mode(struct iwm_softc *, | | 417 | static int iwm_power_mac_update_mode(struct iwm_softc *, |
418 | struct iwm_node *); | | 418 | struct iwm_node *); |
419 | static int iwm_power_update_device(struct iwm_softc *); | | 419 | static int iwm_power_update_device(struct iwm_softc *); |
420 | #ifdef notyet | | 420 | #ifdef notyet |
421 | static int iwm_enable_beacon_filter(struct iwm_softc *, struct iwm_node *); | | 421 | static int iwm_enable_beacon_filter(struct iwm_softc *, struct iwm_node *); |
422 | #endif | | 422 | #endif |
423 | static int iwm_disable_beacon_filter(struct iwm_softc *); | | 423 | static int iwm_disable_beacon_filter(struct iwm_softc *); |
424 | static int iwm_add_sta_cmd(struct iwm_softc *, struct iwm_node *, int); | | 424 | static int iwm_add_sta_cmd(struct iwm_softc *, struct iwm_node *, int); |
425 | static int iwm_add_aux_sta(struct iwm_softc *); | | 425 | static int iwm_add_aux_sta(struct iwm_softc *); |
426 | static uint16_t iwm_scan_rx_chain(struct iwm_softc *); | | 426 | static uint16_t iwm_scan_rx_chain(struct iwm_softc *); |
427 | static uint32_t iwm_scan_rate_n_flags(struct iwm_softc *, int, int); | | 427 | static uint32_t iwm_scan_rate_n_flags(struct iwm_softc *, int, int); |
428 | #ifdef notyet | | 428 | #ifdef notyet |
429 | static uint16_t iwm_get_active_dwell(struct iwm_softc *, int, int); | | 429 | static uint16_t iwm_get_active_dwell(struct iwm_softc *, int, int); |
430 | static uint16_t iwm_get_passive_dwell(struct iwm_softc *, int); | | 430 | static uint16_t iwm_get_passive_dwell(struct iwm_softc *, int); |
431 | #endif | | 431 | #endif |
432 | static uint8_t iwm_lmac_scan_fill_channels(struct iwm_softc *, | | 432 | static uint8_t iwm_lmac_scan_fill_channels(struct iwm_softc *, |
433 | struct iwm_scan_channel_cfg_lmac *, int); | | 433 | struct iwm_scan_channel_cfg_lmac *, int); |
434 | static int iwm_fill_probe_req(struct iwm_softc *, | | 434 | static int iwm_fill_probe_req(struct iwm_softc *, |
435 | struct iwm_scan_probe_req *); | | 435 | struct iwm_scan_probe_req *); |
436 | static int iwm_lmac_scan(struct iwm_softc *); | | 436 | static int iwm_lmac_scan(struct iwm_softc *); |
437 | static int iwm_config_umac_scan(struct iwm_softc *); | | 437 | static int iwm_config_umac_scan(struct iwm_softc *); |
438 | static int iwm_umac_scan(struct iwm_softc *); | | 438 | static int iwm_umac_scan(struct iwm_softc *); |
439 | static uint8_t iwm_ridx2rate(struct ieee80211_rateset *, int); | | 439 | static uint8_t iwm_ridx2rate(struct ieee80211_rateset *, int); |
440 | static void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, | | 440 | static void iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, |
441 | int *); | | 441 | int *); |
442 | static void iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *, | | 442 | static void iwm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *, |
443 | struct iwm_mac_ctx_cmd *, uint32_t, int); | | 443 | struct iwm_mac_ctx_cmd *, uint32_t, int); |
444 | static void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *, | | 444 | static void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *, |
445 | struct iwm_mac_data_sta *, int); | | 445 | struct iwm_mac_data_sta *, int); |
446 | static int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, | | 446 | static int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, |
447 | uint32_t, int); | | 447 | uint32_t, int); |
448 | static int iwm_update_quotas(struct iwm_softc *, struct iwm_node *); | | 448 | static int iwm_update_quotas(struct iwm_softc *, struct iwm_node *); |
449 | static int iwm_auth(struct iwm_softc *); | | 449 | static int iwm_auth(struct iwm_softc *); |
450 | static int iwm_assoc(struct iwm_softc *); | | 450 | static int iwm_assoc(struct iwm_softc *); |
451 | static void iwm_calib_timeout(void *); | | 451 | static void iwm_calib_timeout(void *); |
452 | #ifndef IEEE80211_NO_HT | | 452 | #ifndef IEEE80211_NO_HT |
453 | static void iwm_setrates_task(void *); | | 453 | static void iwm_setrates_task(void *); |
454 | static int iwm_setrates(struct iwm_node *); | | 454 | static int iwm_setrates(struct iwm_node *); |
455 | #endif | | 455 | #endif |
456 | static int iwm_media_change(struct ifnet *); | | 456 | static int iwm_media_change(struct ifnet *); |
457 | static int iwm_do_newstate(struct ieee80211com *, enum ieee80211_state, | | 457 | static int iwm_do_newstate(struct ieee80211com *, enum ieee80211_state, |
458 | int); | | 458 | int); |
459 | static void iwm_newstate_cb(struct work *, void *); | | 459 | static void iwm_newstate_cb(struct work *, void *); |
460 | static int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); | | 460 | static int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); |
461 | static void iwm_endscan(struct iwm_softc *); | | 461 | static void iwm_endscan(struct iwm_softc *); |
462 | static void iwm_fill_sf_command(struct iwm_softc *, struct iwm_sf_cfg_cmd *, | | 462 | static void iwm_fill_sf_command(struct iwm_softc *, struct iwm_sf_cfg_cmd *, |
463 | struct ieee80211_node *); | | 463 | struct ieee80211_node *); |
464 | static int iwm_sf_config(struct iwm_softc *, int); | | 464 | static int iwm_sf_config(struct iwm_softc *, int); |
465 | static int iwm_send_bt_init_conf(struct iwm_softc *); | | 465 | static int iwm_send_bt_init_conf(struct iwm_softc *); |
466 | static int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); | | 466 | static int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); |
467 | static void iwm_tt_tx_backoff(struct iwm_softc *, uint32_t); | | 467 | static void iwm_tt_tx_backoff(struct iwm_softc *, uint32_t); |
468 | static int iwm_init_hw(struct iwm_softc *); | | 468 | static int iwm_init_hw(struct iwm_softc *); |
469 | static int iwm_init(struct ifnet *); | | 469 | static int iwm_init(struct ifnet *); |
470 | static void iwm_start(struct ifnet *); | | 470 | static void iwm_start(struct ifnet *); |
471 | static void iwm_stop(struct ifnet *, int); | | 471 | static void iwm_stop(struct ifnet *, int); |
472 | static void iwm_watchdog(struct ifnet *); | | 472 | static void iwm_watchdog(struct ifnet *); |
473 | static int iwm_ioctl(struct ifnet *, u_long, void *); | | 473 | static int iwm_ioctl(struct ifnet *, u_long, void *); |
474 | #ifdef IWM_DEBUG | | 474 | #ifdef IWM_DEBUG |
475 | static const char *iwm_desc_lookup(uint32_t); | | 475 | static const char *iwm_desc_lookup(uint32_t); |
476 | static void iwm_nic_error(struct iwm_softc *); | | 476 | static void iwm_nic_error(struct iwm_softc *); |
477 | static void iwm_nic_umac_error(struct iwm_softc *); | | 477 | static void iwm_nic_umac_error(struct iwm_softc *); |
478 | #endif | | 478 | #endif |
479 | static void iwm_notif_intr(struct iwm_softc *); | | 479 | static void iwm_notif_intr(struct iwm_softc *); |
480 | static int iwm_intr(void *); | | 480 | static int iwm_intr(void *); |
481 | static void iwm_softintr(void *); | | 481 | static void iwm_softintr(void *); |
482 | static int iwm_preinit(struct iwm_softc *); | | 482 | static int iwm_preinit(struct iwm_softc *); |
483 | static void iwm_attach_hook(device_t); | | 483 | static void iwm_attach_hook(device_t); |
484 | static void iwm_attach(device_t, device_t, void *); | | 484 | static void iwm_attach(device_t, device_t, void *); |
485 | #if 0 | | 485 | #if 0 |
486 | static void iwm_init_task(void *); | | 486 | static void iwm_init_task(void *); |
487 | static int iwm_activate(device_t, enum devact); | | 487 | static int iwm_activate(device_t, enum devact); |
488 | static void iwm_wakeup(struct iwm_softc *); | | 488 | static void iwm_wakeup(struct iwm_softc *); |
489 | #endif | | 489 | #endif |
490 | static void iwm_radiotap_attach(struct iwm_softc *); | | 490 | static void iwm_radiotap_attach(struct iwm_softc *); |
491 | static int iwm_sysctl_fw_loaded_handler(SYSCTLFN_PROTO); | | 491 | static int iwm_sysctl_fw_loaded_handler(SYSCTLFN_PROTO); |
492 | | | 492 | |
493 | static int iwm_sysctl_root_num; | | 493 | static int iwm_sysctl_root_num; |
494 | static int iwm_lar_disable; | | 494 | static int iwm_lar_disable; |
495 | | | 495 | |
496 | #ifndef IWM_DEFAULT_MCC | | 496 | #ifndef IWM_DEFAULT_MCC |
497 | #define IWM_DEFAULT_MCC "ZZ" | | 497 | #define IWM_DEFAULT_MCC "ZZ" |
498 | #endif | | 498 | #endif |
499 | static char iwm_default_mcc[3] = IWM_DEFAULT_MCC; | | 499 | static char iwm_default_mcc[3] = IWM_DEFAULT_MCC; |
500 | | | 500 | |
501 | static int | | 501 | static int |
502 | iwm_firmload(struct iwm_softc *sc) | | 502 | iwm_firmload(struct iwm_softc *sc) |
503 | { | | 503 | { |
504 | struct iwm_fw_info *fw = &sc->sc_fw; | | 504 | struct iwm_fw_info *fw = &sc->sc_fw; |
505 | firmware_handle_t fwh; | | 505 | firmware_handle_t fwh; |
506 | int err; | | 506 | int err; |
507 | | | 507 | |
508 | if (ISSET(sc->sc_flags, IWM_FLAG_FW_LOADED)) | | 508 | if (ISSET(sc->sc_flags, IWM_FLAG_FW_LOADED)) |
509 | return 0; | | 509 | return 0; |
510 | | | 510 | |
511 | /* Open firmware image. */ | | 511 | /* Open firmware image. */ |
512 | err = firmware_open("if_iwm", sc->sc_fwname, &fwh); | | 512 | err = firmware_open("if_iwm", sc->sc_fwname, &fwh); |
513 | if (err) { | | 513 | if (err) { |
514 | aprint_error_dev(sc->sc_dev, | | 514 | aprint_error_dev(sc->sc_dev, |
515 | "could not get firmware handle %s\n", sc->sc_fwname); | | 515 | "could not get firmware handle %s\n", sc->sc_fwname); |
516 | return err; | | 516 | return err; |
517 | } | | 517 | } |
518 | | | 518 | |
519 | if (fw->fw_rawdata != NULL && fw->fw_rawsize > 0) { | | 519 | if (fw->fw_rawdata != NULL && fw->fw_rawsize > 0) { |
520 | kmem_free(fw->fw_rawdata, fw->fw_rawsize); | | 520 | kmem_free(fw->fw_rawdata, fw->fw_rawsize); |
521 | fw->fw_rawdata = NULL; | | 521 | fw->fw_rawdata = NULL; |
522 | } | | 522 | } |
523 | | | 523 | |
524 | fw->fw_rawsize = firmware_get_size(fwh); | | 524 | fw->fw_rawsize = firmware_get_size(fwh); |
525 | /* | | 525 | /* |
526 | * Well, this is how the Linux driver checks it .... | | 526 | * Well, this is how the Linux driver checks it .... |
527 | */ | | 527 | */ |
528 | if (fw->fw_rawsize < sizeof(uint32_t)) { | | 528 | if (fw->fw_rawsize < sizeof(uint32_t)) { |
529 | aprint_error_dev(sc->sc_dev, | | 529 | aprint_error_dev(sc->sc_dev, |
530 | "firmware too short: %zd bytes\n", fw->fw_rawsize); | | 530 | "firmware too short: %zd bytes\n", fw->fw_rawsize); |
531 | err = EINVAL; | | 531 | err = EINVAL; |
532 | goto out; | | 532 | goto out; |
533 | } | | 533 | } |
534 | | | 534 | |
535 | /* Read the firmware. */ | | 535 | /* Read the firmware. */ |
536 | fw->fw_rawdata = kmem_alloc(fw->fw_rawsize, KM_SLEEP); | | 536 | fw->fw_rawdata = kmem_alloc(fw->fw_rawsize, KM_SLEEP); |
537 | err = firmware_read(fwh, 0, fw->fw_rawdata, fw->fw_rawsize); | | 537 | err = firmware_read(fwh, 0, fw->fw_rawdata, fw->fw_rawsize); |
538 | if (err) { | | 538 | if (err) { |
539 | aprint_error_dev(sc->sc_dev, | | 539 | aprint_error_dev(sc->sc_dev, |
540 | "could not read firmware %s\n", sc->sc_fwname); | | 540 | "could not read firmware %s\n", sc->sc_fwname); |
541 | goto out; | | 541 | goto out; |
542 | } | | 542 | } |
543 | | | 543 | |
544 | SET(sc->sc_flags, IWM_FLAG_FW_LOADED); | | 544 | SET(sc->sc_flags, IWM_FLAG_FW_LOADED); |
545 | out: | | 545 | out: |
546 | /* caller will release memory, if necessary */ | | 546 | /* caller will release memory, if necessary */ |
547 | | | 547 | |
548 | firmware_close(fwh); | | 548 | firmware_close(fwh); |
549 | return err; | | 549 | return err; |
550 | } | | 550 | } |
551 | | | 551 | |
552 | /* | | 552 | /* |
553 | * just maintaining status quo. | | 553 | * just maintaining status quo. |
554 | */ | | 554 | */ |
555 | static void | | 555 | static void |
556 | iwm_fix_channel(struct iwm_softc *sc, struct mbuf *m) | | 556 | iwm_fix_channel(struct iwm_softc *sc, struct mbuf *m) |
557 | { | | 557 | { |
558 | struct ieee80211com *ic = &sc->sc_ic; | | 558 | struct ieee80211com *ic = &sc->sc_ic; |
559 | struct ieee80211_frame *wh; | | 559 | struct ieee80211_frame *wh; |
560 | uint8_t subtype; | | 560 | uint8_t subtype; |
561 | | | 561 | |
562 | wh = mtod(m, struct ieee80211_frame *); | | 562 | wh = mtod(m, struct ieee80211_frame *); |
563 | | | 563 | |
564 | if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) | | 564 | if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) |
565 | return; | | 565 | return; |
566 | | | 566 | |
567 | subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; | | 567 | subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
568 | | | 568 | |
569 | if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && | | 569 | if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && |
570 | subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) | | 570 | subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) |
571 | return; | | 571 | return; |
572 | | | 572 | |
573 | int chan = le32toh(sc->sc_last_phy_info.channel); | | 573 | int chan = le32toh(sc->sc_last_phy_info.channel); |
574 | if (chan < __arraycount(ic->ic_channels)) | | 574 | if (chan < __arraycount(ic->ic_channels)) |
575 | ic->ic_curchan = &ic->ic_channels[chan]; | | 575 | ic->ic_curchan = &ic->ic_channels[chan]; |
576 | } | | 576 | } |
577 | | | 577 | |
578 | static int | | 578 | static int |
579 | iwm_store_cscheme(struct iwm_softc *sc, uint8_t *data, size_t dlen) | | 579 | iwm_store_cscheme(struct iwm_softc *sc, uint8_t *data, size_t dlen) |
580 | { | | 580 | { |
581 | struct iwm_fw_cscheme_list *l = (struct iwm_fw_cscheme_list *)data; | | 581 | struct iwm_fw_cscheme_list *l = (struct iwm_fw_cscheme_list *)data; |
582 | | | 582 | |
583 | if (dlen < sizeof(*l) || | | 583 | if (dlen < sizeof(*l) || |
584 | dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) | | 584 | dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) |
585 | return EINVAL; | | 585 | return EINVAL; |
586 | | | 586 | |
587 | /* we don't actually store anything for now, always use s/w crypto */ | | 587 | /* we don't actually store anything for now, always use s/w crypto */ |
588 | | | 588 | |
589 | return 0; | | 589 | return 0; |
590 | } | | 590 | } |
591 | | | 591 | |
592 | static int | | 592 | static int |
593 | iwm_firmware_store_section(struct iwm_softc *sc, enum iwm_ucode_type type, | | 593 | iwm_firmware_store_section(struct iwm_softc *sc, enum iwm_ucode_type type, |
594 | uint8_t *data, size_t dlen) | | 594 | uint8_t *data, size_t dlen) |
595 | { | | 595 | { |
596 | struct iwm_fw_sects *fws; | | 596 | struct iwm_fw_sects *fws; |
597 | struct iwm_fw_onesect *fwone; | | 597 | struct iwm_fw_onesect *fwone; |
598 | | | 598 | |
599 | if (type >= IWM_UCODE_TYPE_MAX) | | 599 | if (type >= IWM_UCODE_TYPE_MAX) |
600 | return EINVAL; | | 600 | return EINVAL; |
601 | if (dlen < sizeof(uint32_t)) | | 601 | if (dlen < sizeof(uint32_t)) |
602 | return EINVAL; | | 602 | return EINVAL; |
603 | | | 603 | |
604 | fws = &sc->sc_fw.fw_sects[type]; | | 604 | fws = &sc->sc_fw.fw_sects[type]; |
605 | if (fws->fw_count >= IWM_UCODE_SECT_MAX) | | 605 | if (fws->fw_count >= IWM_UCODE_SECT_MAX) |
606 | return EINVAL; | | 606 | return EINVAL; |
607 | | | 607 | |
608 | fwone = &fws->fw_sect[fws->fw_count]; | | 608 | fwone = &fws->fw_sect[fws->fw_count]; |
609 | | | 609 | |
610 | /* first 32bit are device load offset */ | | 610 | /* first 32bit are device load offset */ |
611 | memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); | | 611 | memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); |
612 | | | 612 | |
613 | /* rest is data */ | | 613 | /* rest is data */ |
614 | fwone->fws_data = data + sizeof(uint32_t); | | 614 | fwone->fws_data = data + sizeof(uint32_t); |
615 | fwone->fws_len = dlen - sizeof(uint32_t); | | 615 | fwone->fws_len = dlen - sizeof(uint32_t); |
616 | | | 616 | |
617 | /* for freeing the buffer during driver unload */ | | 617 | /* for freeing the buffer during driver unload */ |
618 | fwone->fws_alloc = data; | | 618 | fwone->fws_alloc = data; |
619 | fwone->fws_allocsize = dlen; | | 619 | fwone->fws_allocsize = dlen; |
620 | | | 620 | |
621 | fws->fw_count++; | | 621 | fws->fw_count++; |
622 | fws->fw_totlen += fwone->fws_len; | | 622 | fws->fw_totlen += fwone->fws_len; |
623 | | | 623 | |
624 | return 0; | | 624 | return 0; |
625 | } | | 625 | } |
626 | | | 626 | |
627 | struct iwm_tlv_calib_data { | | 627 | struct iwm_tlv_calib_data { |
628 | uint32_t ucode_type; | | 628 | uint32_t ucode_type; |
629 | struct iwm_tlv_calib_ctrl calib; | | 629 | struct iwm_tlv_calib_ctrl calib; |
630 | } __packed; | | 630 | } __packed; |
631 | | | 631 | |
632 | static int | | 632 | static int |
633 | iwm_set_default_calib(struct iwm_softc *sc, const void *data) | | 633 | iwm_set_default_calib(struct iwm_softc *sc, const void *data) |
634 | { | | 634 | { |
635 | const struct iwm_tlv_calib_data *def_calib = data; | | 635 | const struct iwm_tlv_calib_data *def_calib = data; |
636 | uint32_t ucode_type = le32toh(def_calib->ucode_type); | | 636 | uint32_t ucode_type = le32toh(def_calib->ucode_type); |
637 | | | 637 | |
638 | if (ucode_type >= IWM_UCODE_TYPE_MAX) { | | 638 | if (ucode_type >= IWM_UCODE_TYPE_MAX) { |
639 | DPRINTF(("%s: Wrong ucode_type %u for default calibration.\n", | | 639 | DPRINTF(("%s: Wrong ucode_type %u for default calibration.\n", |
640 | DEVNAME(sc), ucode_type)); | | 640 | DEVNAME(sc), ucode_type)); |
641 | return EINVAL; | | 641 | return EINVAL; |
642 | } | | 642 | } |
643 | | | 643 | |
644 | sc->sc_default_calib[ucode_type].flow_trigger = | | 644 | sc->sc_default_calib[ucode_type].flow_trigger = |
645 | def_calib->calib.flow_trigger; | | 645 | def_calib->calib.flow_trigger; |
646 | sc->sc_default_calib[ucode_type].event_trigger = | | 646 | sc->sc_default_calib[ucode_type].event_trigger = |
647 | def_calib->calib.event_trigger; | | 647 | def_calib->calib.event_trigger; |
648 | | | 648 | |
649 | return 0; | | 649 | return 0; |
650 | } | | 650 | } |
651 | | | 651 | |
652 | static int | | 652 | static int |
653 | iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) | | 653 | iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) |
654 | { | | 654 | { |
655 | struct iwm_fw_info *fw = &sc->sc_fw; | | 655 | struct iwm_fw_info *fw = &sc->sc_fw; |
656 | struct iwm_tlv_ucode_header *uhdr; | | 656 | struct iwm_tlv_ucode_header *uhdr; |
657 | struct iwm_ucode_tlv tlv; | | 657 | struct iwm_ucode_tlv tlv; |
658 | enum iwm_ucode_tlv_type tlv_type; | | 658 | enum iwm_ucode_tlv_type tlv_type; |
659 | uint8_t *data; | | 659 | uint8_t *data; |
660 | int err, status; | | 660 | int err, status; |
661 | size_t len; | | 661 | size_t len; |
662 | | | 662 | |
663 | if (ucode_type != IWM_UCODE_TYPE_INIT && | | 663 | if (ucode_type != IWM_UCODE_TYPE_INIT && |
664 | fw->fw_status == IWM_FW_STATUS_DONE) | | 664 | fw->fw_status == IWM_FW_STATUS_DONE) |
665 | return 0; | | 665 | return 0; |
666 | | | 666 | |
667 | if (fw->fw_status == IWM_FW_STATUS_NONE) { | | 667 | if (fw->fw_status == IWM_FW_STATUS_NONE) { |
668 | fw->fw_status = IWM_FW_STATUS_INPROGRESS; | | 668 | fw->fw_status = IWM_FW_STATUS_INPROGRESS; |
669 | } else { | | 669 | } else { |
670 | while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) | | 670 | while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) |
671 | tsleep(&sc->sc_fw, 0, "iwmfwp", 0); | | 671 | tsleep(&sc->sc_fw, 0, "iwmfwp", 0); |
672 | } | | 672 | } |
673 | status = fw->fw_status; | | 673 | status = fw->fw_status; |
674 | | | 674 | |
675 | if (status == IWM_FW_STATUS_DONE) | | 675 | if (status == IWM_FW_STATUS_DONE) |
676 | return 0; | | 676 | return 0; |
677 | | | 677 | |
678 | err = iwm_firmload(sc); | | 678 | err = iwm_firmload(sc); |
679 | if (err) { | | 679 | if (err) { |
680 | aprint_error_dev(sc->sc_dev, | | 680 | aprint_error_dev(sc->sc_dev, |
681 | "could not read firmware %s (error %d)\n", | | 681 | "could not read firmware %s (error %d)\n", |
682 | sc->sc_fwname, err); | | 682 | sc->sc_fwname, err); |
683 | goto out; | | 683 | goto out; |
684 | } | | 684 | } |
685 | | | 685 | |
686 | sc->sc_capaflags = 0; | | 686 | sc->sc_capaflags = 0; |
687 | sc->sc_capa_n_scan_channels = IWM_MAX_NUM_SCAN_CHANNELS; | | 687 | sc->sc_capa_n_scan_channels = IWM_MAX_NUM_SCAN_CHANNELS; |
688 | memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa)); | | 688 | memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa)); |
689 | memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); | | 689 | memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); |
690 | | | 690 | |
691 | uhdr = (void *)fw->fw_rawdata; | | 691 | uhdr = (void *)fw->fw_rawdata; |
692 | if (*(uint32_t *)fw->fw_rawdata != 0 | | 692 | if (*(uint32_t *)fw->fw_rawdata != 0 |
693 | || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { | | 693 | || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { |
694 | aprint_error_dev(sc->sc_dev, "invalid firmware %s\n", | | 694 | aprint_error_dev(sc->sc_dev, "invalid firmware %s\n", |
695 | sc->sc_fwname); | | 695 | sc->sc_fwname); |
696 | err = EINVAL; | | 696 | err = EINVAL; |
697 | goto out; | | 697 | goto out; |
698 | } | | 698 | } |
699 | | | 699 | |
700 | snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", | | 700 | snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", |
701 | IWM_UCODE_MAJOR(le32toh(uhdr->ver)), | | 701 | IWM_UCODE_MAJOR(le32toh(uhdr->ver)), |
702 | IWM_UCODE_MINOR(le32toh(uhdr->ver)), | | 702 | IWM_UCODE_MINOR(le32toh(uhdr->ver)), |
703 | IWM_UCODE_API(le32toh(uhdr->ver))); | | 703 | IWM_UCODE_API(le32toh(uhdr->ver))); |
704 | data = uhdr->data; | | 704 | data = uhdr->data; |
705 | len = fw->fw_rawsize - sizeof(*uhdr); | | 705 | len = fw->fw_rawsize - sizeof(*uhdr); |
706 | | | 706 | |
707 | while (len >= sizeof(tlv)) { | | 707 | while (len >= sizeof(tlv)) { |
708 | size_t tlv_len; | | 708 | size_t tlv_len; |
709 | void *tlv_data; | | 709 | void *tlv_data; |
710 | | | 710 | |
711 | memcpy(&tlv, data, sizeof(tlv)); | | 711 | memcpy(&tlv, data, sizeof(tlv)); |
712 | tlv_len = le32toh(tlv.length); | | 712 | tlv_len = le32toh(tlv.length); |
713 | tlv_type = le32toh(tlv.type); | | 713 | tlv_type = le32toh(tlv.type); |
714 | | | 714 | |
715 | len -= sizeof(tlv); | | 715 | len -= sizeof(tlv); |
716 | data += sizeof(tlv); | | 716 | data += sizeof(tlv); |
717 | tlv_data = data; | | 717 | tlv_data = data; |
718 | | | 718 | |
719 | if (len < tlv_len) { | | 719 | if (len < tlv_len) { |
720 | aprint_error_dev(sc->sc_dev, | | 720 | aprint_error_dev(sc->sc_dev, |
721 | "firmware too short: %zu bytes\n", len); | | 721 | "firmware too short: %zu bytes\n", len); |
722 | err = EINVAL; | | 722 | err = EINVAL; |
723 | goto parse_out; | | 723 | goto parse_out; |
724 | } | | 724 | } |
725 | | | 725 | |
726 | switch (tlv_type) { | | 726 | switch (tlv_type) { |
727 | case IWM_UCODE_TLV_PROBE_MAX_LEN: | | 727 | case IWM_UCODE_TLV_PROBE_MAX_LEN: |
728 | if (tlv_len < sizeof(uint32_t)) { | | 728 | if (tlv_len < sizeof(uint32_t)) { |
729 | err = EINVAL; | | 729 | err = EINVAL; |
730 | goto parse_out; | | 730 | goto parse_out; |
731 | } | | 731 | } |
732 | sc->sc_capa_max_probe_len | | 732 | sc->sc_capa_max_probe_len |
733 | = le32toh(*(uint32_t *)tlv_data); | | 733 | = le32toh(*(uint32_t *)tlv_data); |
734 | /* limit it to something sensible */ | | 734 | /* limit it to something sensible */ |
735 | if (sc->sc_capa_max_probe_len > | | 735 | if (sc->sc_capa_max_probe_len > |
736 | IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { | | 736 | IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { |
737 | err = EINVAL; | | 737 | err = EINVAL; |
738 | goto parse_out; | | 738 | goto parse_out; |
739 | } | | 739 | } |
740 | break; | | 740 | break; |
741 | case IWM_UCODE_TLV_PAN: | | 741 | case IWM_UCODE_TLV_PAN: |
742 | if (tlv_len) { | | 742 | if (tlv_len) { |
743 | err = EINVAL; | | 743 | err = EINVAL; |
744 | goto parse_out; | | 744 | goto parse_out; |
745 | } | | 745 | } |
746 | sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; | | 746 | sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; |
747 | break; | | 747 | break; |
748 | case IWM_UCODE_TLV_FLAGS: | | 748 | case IWM_UCODE_TLV_FLAGS: |
749 | if (tlv_len < sizeof(uint32_t)) { | | 749 | if (tlv_len < sizeof(uint32_t)) { |
750 | err = EINVAL; | | 750 | err = EINVAL; |
751 | goto parse_out; | | 751 | goto parse_out; |
752 | } | | 752 | } |
753 | if (tlv_len % sizeof(uint32_t)) { | | 753 | if (tlv_len % sizeof(uint32_t)) { |
754 | err = EINVAL; | | 754 | err = EINVAL; |
755 | goto parse_out; | | 755 | goto parse_out; |
756 | } | | 756 | } |
757 | /* | | 757 | /* |
758 | * Apparently there can be many flags, but Linux driver | | 758 | * Apparently there can be many flags, but Linux driver |
759 | * parses only the first one, and so do we. | | 759 | * parses only the first one, and so do we. |
760 | * | | 760 | * |
761 | * XXX: why does this override IWM_UCODE_TLV_PAN? | | 761 | * XXX: why does this override IWM_UCODE_TLV_PAN? |
762 | * Intentional or a bug? Observations from | | 762 | * Intentional or a bug? Observations from |
763 | * current firmware file: | | 763 | * current firmware file: |
764 | * 1) TLV_PAN is parsed first | | 764 | * 1) TLV_PAN is parsed first |
765 | * 2) TLV_FLAGS contains TLV_FLAGS_PAN | | 765 | * 2) TLV_FLAGS contains TLV_FLAGS_PAN |
766 | * ==> this resets TLV_PAN to itself... hnnnk | | 766 | * ==> this resets TLV_PAN to itself... hnnnk |
767 | */ | | 767 | */ |
768 | sc->sc_capaflags = le32toh(*(uint32_t *)tlv_data); | | 768 | sc->sc_capaflags = le32toh(*(uint32_t *)tlv_data); |
769 | break; | | 769 | break; |
770 | case IWM_UCODE_TLV_CSCHEME: | | 770 | case IWM_UCODE_TLV_CSCHEME: |
771 | err = iwm_store_cscheme(sc, tlv_data, tlv_len); | | 771 | err = iwm_store_cscheme(sc, tlv_data, tlv_len); |
772 | if (err) | | 772 | if (err) |
773 | goto parse_out; | | 773 | goto parse_out; |
774 | break; | | 774 | break; |
775 | case IWM_UCODE_TLV_NUM_OF_CPU: { | | 775 | case IWM_UCODE_TLV_NUM_OF_CPU: { |
776 | uint32_t num_cpu; | | 776 | uint32_t num_cpu; |
777 | if (tlv_len != sizeof(uint32_t)) { | | 777 | if (tlv_len != sizeof(uint32_t)) { |
778 | err = EINVAL; | | 778 | err = EINVAL; |
779 | goto parse_out; | | 779 | goto parse_out; |
780 | } | | 780 | } |
781 | num_cpu = le32toh(*(uint32_t *)tlv_data); | | 781 | num_cpu = le32toh(*(uint32_t *)tlv_data); |
782 | if (num_cpu == 2) { | | 782 | if (num_cpu == 2) { |
783 | fw->fw_sects[IWM_UCODE_TYPE_REGULAR].is_dual_cpus = | | 783 | fw->fw_sects[IWM_UCODE_TYPE_REGULAR].is_dual_cpus = |
784 | true; | | 784 | true; |
785 | fw->fw_sects[IWM_UCODE_TYPE_INIT].is_dual_cpus = | | 785 | fw->fw_sects[IWM_UCODE_TYPE_INIT].is_dual_cpus = |
786 | true; | | 786 | true; |
787 | fw->fw_sects[IWM_UCODE_TYPE_WOW].is_dual_cpus = | | 787 | fw->fw_sects[IWM_UCODE_TYPE_WOW].is_dual_cpus = |
788 | true; | | 788 | true; |
789 | } else if (num_cpu < 1 || num_cpu > 2) { | | 789 | } else if (num_cpu < 1 || num_cpu > 2) { |
790 | err = EINVAL; | | 790 | err = EINVAL; |
791 | goto parse_out; | | 791 | goto parse_out; |
792 | } | | 792 | } |
793 | break; | | 793 | break; |
794 | } | | 794 | } |
795 | case IWM_UCODE_TLV_SEC_RT: | | 795 | case IWM_UCODE_TLV_SEC_RT: |
796 | err = iwm_firmware_store_section(sc, | | 796 | err = iwm_firmware_store_section(sc, |
797 | IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len); | | 797 | IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len); |
798 | if (err) | | 798 | if (err) |
799 | goto parse_out; | | 799 | goto parse_out; |
800 | break; | | 800 | break; |
801 | case IWM_UCODE_TLV_SEC_INIT: | | 801 | case IWM_UCODE_TLV_SEC_INIT: |
802 | err = iwm_firmware_store_section(sc, | | 802 | err = iwm_firmware_store_section(sc, |
803 | IWM_UCODE_TYPE_INIT, tlv_data, tlv_len); | | 803 | IWM_UCODE_TYPE_INIT, tlv_data, tlv_len); |
804 | if (err) | | 804 | if (err) |
805 | goto parse_out; | | 805 | goto parse_out; |
806 | break; | | 806 | break; |
807 | case IWM_UCODE_TLV_SEC_WOWLAN: | | 807 | case IWM_UCODE_TLV_SEC_WOWLAN: |
808 | err = iwm_firmware_store_section(sc, | | 808 | err = iwm_firmware_store_section(sc, |
809 | IWM_UCODE_TYPE_WOW, tlv_data, tlv_len); | | 809 | IWM_UCODE_TYPE_WOW, tlv_data, tlv_len); |
810 | if (err) | | 810 | if (err) |
811 | goto parse_out; | | 811 | goto parse_out; |
812 | break; | | 812 | break; |
813 | case IWM_UCODE_TLV_DEF_CALIB: | | 813 | case IWM_UCODE_TLV_DEF_CALIB: |
814 | if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { | | 814 | if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { |
815 | err = EINVAL; | | 815 | err = EINVAL; |
816 | goto parse_out; | | 816 | goto parse_out; |
817 | } | | 817 | } |
818 | err = iwm_set_default_calib(sc, tlv_data); | | 818 | err = iwm_set_default_calib(sc, tlv_data); |
819 | if (err) | | 819 | if (err) |
820 | goto parse_out; | | 820 | goto parse_out; |
821 | break; | | 821 | break; |
822 | case IWM_UCODE_TLV_PHY_SKU: | | 822 | case IWM_UCODE_TLV_PHY_SKU: |
823 | if (tlv_len != sizeof(uint32_t)) { | | 823 | if (tlv_len != sizeof(uint32_t)) { |
824 | err = EINVAL; | | 824 | err = EINVAL; |
825 | goto parse_out; | | 825 | goto parse_out; |
826 | } | | 826 | } |
827 | sc->sc_fw_phy_config = le32toh(*(uint32_t *)tlv_data); | | 827 | sc->sc_fw_phy_config = le32toh(*(uint32_t *)tlv_data); |
828 | break; | | 828 | break; |
829 | | | 829 | |
830 | case IWM_UCODE_TLV_API_CHANGES_SET: { | | 830 | case IWM_UCODE_TLV_API_CHANGES_SET: { |
831 | struct iwm_ucode_api *api; | | 831 | struct iwm_ucode_api *api; |
832 | uint32_t idx, bits; | | 832 | uint32_t idx, bits; |
833 | int i; | | 833 | int i; |
834 | if (tlv_len != sizeof(*api)) { | | 834 | if (tlv_len != sizeof(*api)) { |
835 | err = EINVAL; | | 835 | err = EINVAL; |
836 | goto parse_out; | | 836 | goto parse_out; |
837 | } | | 837 | } |
838 | api = (struct iwm_ucode_api *)tlv_data; | | 838 | api = (struct iwm_ucode_api *)tlv_data; |
839 | idx = le32toh(api->api_index); | | 839 | idx = le32toh(api->api_index); |
840 | bits = le32toh(api->api_flags); | | 840 | bits = le32toh(api->api_flags); |
841 | if (idx >= howmany(IWM_NUM_UCODE_TLV_API, 32)) { | | 841 | if (idx >= howmany(IWM_NUM_UCODE_TLV_API, 32)) { |
842 | err = EINVAL; | | 842 | err = EINVAL; |
843 | goto parse_out; | | 843 | goto parse_out; |
844 | } | | 844 | } |
845 | for (i = 0; i < 32; i++) { | | 845 | for (i = 0; i < 32; i++) { |
846 | if (!ISSET(bits, __BIT(i))) | | 846 | if (!ISSET(bits, __BIT(i))) |
847 | continue; | | 847 | continue; |
848 | setbit(sc->sc_ucode_api, i + (32 * idx)); | | 848 | setbit(sc->sc_ucode_api, i + (32 * idx)); |
849 | } | | 849 | } |
850 | break; | | 850 | break; |
851 | } | | 851 | } |
852 | | | 852 | |
853 | case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { | | 853 | case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { |
854 | struct iwm_ucode_capa *capa; | | 854 | struct iwm_ucode_capa *capa; |
855 | uint32_t idx, bits; | | 855 | uint32_t idx, bits; |
856 | int i; | | 856 | int i; |
857 | if (tlv_len != sizeof(*capa)) { | | 857 | if (tlv_len != sizeof(*capa)) { |
858 | err = EINVAL; | | 858 | err = EINVAL; |
859 | goto parse_out; | | 859 | goto parse_out; |
860 | } | | 860 | } |
861 | capa = (struct iwm_ucode_capa *)tlv_data; | | 861 | capa = (struct iwm_ucode_capa *)tlv_data; |
862 | idx = le32toh(capa->api_index); | | 862 | idx = le32toh(capa->api_index); |
863 | bits = le32toh(capa->api_capa); | | 863 | bits = le32toh(capa->api_capa); |
864 | if (idx >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { | | 864 | if (idx >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { |
865 | err = EINVAL; | | 865 | err = EINVAL; |
866 | goto parse_out; | | 866 | goto parse_out; |
867 | } | | 867 | } |
868 | for (i = 0; i < 32; i++) { | | 868 | for (i = 0; i < 32; i++) { |
869 | if (!ISSET(bits, __BIT(i))) | | 869 | if (!ISSET(bits, __BIT(i))) |
870 | continue; | | 870 | continue; |
871 | setbit(sc->sc_enabled_capa, i + (32 * idx)); | | 871 | setbit(sc->sc_enabled_capa, i + (32 * idx)); |
872 | } | | 872 | } |
873 | break; | | 873 | break; |
874 | } | | 874 | } |
875 | | | 875 | |
876 | case IWM_UCODE_TLV_FW_UNDOCUMENTED1: | | 876 | case IWM_UCODE_TLV_FW_UNDOCUMENTED1: |
877 | case IWM_UCODE_TLV_SDIO_ADMA_ADDR: | | 877 | case IWM_UCODE_TLV_SDIO_ADMA_ADDR: |
878 | case IWM_UCODE_TLV_FW_GSCAN_CAPA: | | 878 | case IWM_UCODE_TLV_FW_GSCAN_CAPA: |
879 | case IWM_UCODE_TLV_FW_MEM_SEG: | | 879 | case IWM_UCODE_TLV_FW_MEM_SEG: |
880 | /* ignore, not used by current driver */ | | 880 | /* ignore, not used by current driver */ |
881 | break; | | 881 | break; |
882 | | | 882 | |
883 | case IWM_UCODE_TLV_SEC_RT_USNIFFER: | | 883 | case IWM_UCODE_TLV_SEC_RT_USNIFFER: |
884 | err = iwm_firmware_store_section(sc, | | 884 | err = iwm_firmware_store_section(sc, |
885 | IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data, | | 885 | IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data, |
886 | tlv_len); | | 886 | tlv_len); |
887 | if (err) | | 887 | if (err) |
888 | goto parse_out; | | 888 | goto parse_out; |
889 | break; | | 889 | break; |
890 | | | 890 | |
891 | case IWM_UCODE_TLV_PAGING: { | | 891 | case IWM_UCODE_TLV_PAGING: { |
892 | uint32_t paging_mem_size; | | 892 | uint32_t paging_mem_size; |
893 | if (tlv_len != sizeof(paging_mem_size)) { | | 893 | if (tlv_len != sizeof(paging_mem_size)) { |
894 | err = EINVAL; | | 894 | err = EINVAL; |
895 | goto parse_out; | | 895 | goto parse_out; |
896 | } | | 896 | } |
897 | paging_mem_size = le32toh(*(uint32_t *)tlv_data); | | 897 | paging_mem_size = le32toh(*(uint32_t *)tlv_data); |
898 | if (paging_mem_size > IWM_MAX_PAGING_IMAGE_SIZE) { | | 898 | if (paging_mem_size > IWM_MAX_PAGING_IMAGE_SIZE) { |
899 | err = EINVAL; | | 899 | err = EINVAL; |
900 | goto parse_out; | | 900 | goto parse_out; |
901 | } | | 901 | } |
902 | if (paging_mem_size & (IWM_FW_PAGING_SIZE - 1)) { | | 902 | if (paging_mem_size & (IWM_FW_PAGING_SIZE - 1)) { |
903 | err = EINVAL; | | 903 | err = EINVAL; |
904 | goto parse_out; | | 904 | goto parse_out; |
905 | } | | 905 | } |
906 | fw->fw_sects[IWM_UCODE_TYPE_REGULAR].paging_mem_size = | | 906 | fw->fw_sects[IWM_UCODE_TYPE_REGULAR].paging_mem_size = |
907 | paging_mem_size; | | 907 | paging_mem_size; |
908 | fw->fw_sects[IWM_UCODE_TYPE_REGULAR_USNIFFER].paging_mem_size = | | 908 | fw->fw_sects[IWM_UCODE_TYPE_REGULAR_USNIFFER].paging_mem_size = |
909 | paging_mem_size; | | 909 | paging_mem_size; |
910 | break; | | 910 | break; |
911 | } | | 911 | } |
912 | | | 912 | |
913 | case IWM_UCODE_TLV_N_SCAN_CHANNELS: | | 913 | case IWM_UCODE_TLV_N_SCAN_CHANNELS: |
914 | if (tlv_len != sizeof(uint32_t)) { | | 914 | if (tlv_len != sizeof(uint32_t)) { |
915 | err = EINVAL; | | 915 | err = EINVAL; |
916 | goto parse_out; | | 916 | goto parse_out; |
917 | } | | 917 | } |
918 | sc->sc_capa_n_scan_channels = | | 918 | sc->sc_capa_n_scan_channels = |
919 | le32toh(*(uint32_t *)tlv_data); | | 919 | le32toh(*(uint32_t *)tlv_data); |
920 | break; | | 920 | break; |
921 | | | 921 | |
922 | case IWM_UCODE_TLV_FW_VERSION: | | 922 | case IWM_UCODE_TLV_FW_VERSION: |
923 | if (tlv_len != sizeof(uint32_t) * 3) { | | 923 | if (tlv_len != sizeof(uint32_t) * 3) { |
924 | err = EINVAL; | | 924 | err = EINVAL; |
925 | goto parse_out; | | 925 | goto parse_out; |
926 | } | | 926 | } |
927 | snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), | | 927 | snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), |
928 | "%d.%d.%d", | | 928 | "%d.%d.%d", |
929 | le32toh(((uint32_t *)tlv_data)[0]), | | 929 | le32toh(((uint32_t *)tlv_data)[0]), |
930 | le32toh(((uint32_t *)tlv_data)[1]), | | 930 | le32toh(((uint32_t *)tlv_data)[1]), |
931 | le32toh(((uint32_t *)tlv_data)[2])); | | 931 | le32toh(((uint32_t *)tlv_data)[2])); |
932 | break; | | 932 | break; |
933 | | | 933 | |
934 | default: | | 934 | default: |
935 | DPRINTF(("%s: unknown firmware section %d, abort\n", | | 935 | DPRINTF(("%s: unknown firmware section %d, abort\n", |
936 | DEVNAME(sc), tlv_type)); | | 936 | DEVNAME(sc), tlv_type)); |
937 | err = EINVAL; | | 937 | err = EINVAL; |
938 | goto parse_out; | | 938 | goto parse_out; |
939 | } | | 939 | } |
940 | | | 940 | |
941 | len -= roundup(tlv_len, 4); | | 941 | len -= roundup(tlv_len, 4); |
942 | data += roundup(tlv_len, 4); | | 942 | data += roundup(tlv_len, 4); |
943 | } | | 943 | } |
944 | | | 944 | |
945 | KASSERT(err == 0); | | 945 | KASSERT(err == 0); |
946 | | | 946 | |
947 | parse_out: | | 947 | parse_out: |
948 | if (err) { | | 948 | if (err) { |
949 | aprint_error_dev(sc->sc_dev, | | 949 | aprint_error_dev(sc->sc_dev, |
950 | "firmware parse error, section type %d\n", tlv_type); | | 950 | "firmware parse error, section type %d\n", tlv_type); |
951 | } | | 951 | } |
952 | | | 952 | |
953 | if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { | | 953 | if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { |
954 | aprint_error_dev(sc->sc_dev, | | 954 | aprint_error_dev(sc->sc_dev, |
955 | "device uses unsupported power ops\n"); | | 955 | "device uses unsupported power ops\n"); |
956 | err = ENOTSUP; | | 956 | err = ENOTSUP; |
957 | } | | 957 | } |
958 | | | 958 | |
959 | out: | | 959 | out: |
960 | if (err) | | 960 | if (err) |
961 | fw->fw_status = IWM_FW_STATUS_NONE; | | 961 | fw->fw_status = IWM_FW_STATUS_NONE; |
962 | else | | 962 | else |
963 | fw->fw_status = IWM_FW_STATUS_DONE; | | 963 | fw->fw_status = IWM_FW_STATUS_DONE; |
964 | wakeup(&sc->sc_fw); | | 964 | wakeup(&sc->sc_fw); |
965 | | | 965 | |
966 | if (err && fw->fw_rawdata != NULL) { | | 966 | if (err && fw->fw_rawdata != NULL) { |
967 | kmem_free(fw->fw_rawdata, fw->fw_rawsize); | | 967 | kmem_free(fw->fw_rawdata, fw->fw_rawsize); |
968 | fw->fw_rawdata = NULL; | | 968 | fw->fw_rawdata = NULL; |
969 | CLR(sc->sc_flags, IWM_FLAG_FW_LOADED); | | 969 | CLR(sc->sc_flags, IWM_FLAG_FW_LOADED); |
970 | /* don't touch fw->fw_status */ | | 970 | /* don't touch fw->fw_status */ |
971 | memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); | | 971 | memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); |
972 | } | | 972 | } |
973 | return err; | | 973 | return err; |
974 | } | | 974 | } |
975 | | | 975 | |
976 | static uint32_t | | 976 | static uint32_t |
977 | iwm_read_prph(struct iwm_softc *sc, uint32_t addr) | | 977 | iwm_read_prph(struct iwm_softc *sc, uint32_t addr) |
978 | { | | 978 | { |
979 | IWM_WRITE(sc, | | 979 | IWM_WRITE(sc, |
980 | IWM_HBUS_TARG_PRPH_RADDR, ((addr & 0x000fffff) | (3 << 24))); | | 980 | IWM_HBUS_TARG_PRPH_RADDR, ((addr & 0x000fffff) | (3 << 24))); |
981 | IWM_BARRIER_READ_WRITE(sc); | | 981 | IWM_BARRIER_READ_WRITE(sc); |
982 | return IWM_READ(sc, IWM_HBUS_TARG_PRPH_RDAT); | | 982 | return IWM_READ(sc, IWM_HBUS_TARG_PRPH_RDAT); |
983 | } | | 983 | } |
984 | | | 984 | |
985 | static void | | 985 | static void |
986 | iwm_write_prph(struct iwm_softc *sc, uint32_t addr, uint32_t val) | | 986 | iwm_write_prph(struct iwm_softc *sc, uint32_t addr, uint32_t val) |
987 | { | | 987 | { |
988 | IWM_WRITE(sc, | | 988 | IWM_WRITE(sc, |
989 | IWM_HBUS_TARG_PRPH_WADDR, ((addr & 0x000fffff) | (3 << 24))); | | 989 | IWM_HBUS_TARG_PRPH_WADDR, ((addr & 0x000fffff) | (3 << 24))); |
990 | IWM_BARRIER_WRITE(sc); | | 990 | IWM_BARRIER_WRITE(sc); |
991 | IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WDAT, val); | | 991 | IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WDAT, val); |
992 | } | | 992 | } |
993 | | | 993 | |
994 | #ifdef IWM_DEBUG | | 994 | #ifdef IWM_DEBUG |
995 | static int | | 995 | static int |
996 | iwm_read_mem(struct iwm_softc *sc, uint32_t addr, void *buf, int dwords) | | 996 | iwm_read_mem(struct iwm_softc *sc, uint32_t addr, void *buf, int dwords) |
997 | { | | 997 | { |
998 | int offs; | | 998 | int offs; |
999 | uint32_t *vals = buf; | | 999 | uint32_t *vals = buf; |
1000 | | | 1000 | |
1001 | if (iwm_nic_lock(sc)) { | | 1001 | if (iwm_nic_lock(sc)) { |
1002 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_RADDR, addr); | | 1002 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_RADDR, addr); |
1003 | for (offs = 0; offs < dwords; offs++) | | 1003 | for (offs = 0; offs < dwords; offs++) |
1004 | vals[offs] = IWM_READ(sc, IWM_HBUS_TARG_MEM_RDAT); | | 1004 | vals[offs] = IWM_READ(sc, IWM_HBUS_TARG_MEM_RDAT); |
1005 | iwm_nic_unlock(sc); | | 1005 | iwm_nic_unlock(sc); |
1006 | return 0; | | 1006 | return 0; |
1007 | } | | 1007 | } |
1008 | return EBUSY; | | 1008 | return EBUSY; |
1009 | } | | 1009 | } |
1010 | #endif | | 1010 | #endif |
1011 | | | 1011 | |
1012 | static int | | 1012 | static int |
1013 | iwm_write_mem(struct iwm_softc *sc, uint32_t addr, const void *buf, int dwords) | | 1013 | iwm_write_mem(struct iwm_softc *sc, uint32_t addr, const void *buf, int dwords) |
1014 | { | | 1014 | { |
1015 | int offs; | | 1015 | int offs; |
1016 | const uint32_t *vals = buf; | | 1016 | const uint32_t *vals = buf; |
1017 | | | 1017 | |
1018 | if (iwm_nic_lock(sc)) { | | 1018 | if (iwm_nic_lock(sc)) { |
1019 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WADDR, addr); | | 1019 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WADDR, addr); |
1020 | /* WADDR auto-increments */ | | 1020 | /* WADDR auto-increments */ |
1021 | for (offs = 0; offs < dwords; offs++) { | | 1021 | for (offs = 0; offs < dwords; offs++) { |
1022 | uint32_t val = vals ? vals[offs] : 0; | | 1022 | uint32_t val = vals ? vals[offs] : 0; |
1023 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WDAT, val); | | 1023 | IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WDAT, val); |
1024 | } | | 1024 | } |
1025 | iwm_nic_unlock(sc); | | 1025 | iwm_nic_unlock(sc); |
1026 | return 0; | | 1026 | return 0; |
1027 | } | | 1027 | } |
1028 | return EBUSY; | | 1028 | return EBUSY; |
1029 | } | | 1029 | } |
1030 | | | 1030 | |
1031 | static int | | 1031 | static int |
1032 | iwm_write_mem32(struct iwm_softc *sc, uint32_t addr, uint32_t val) | | 1032 | iwm_write_mem32(struct iwm_softc *sc, uint32_t addr, uint32_t val) |
1033 | { | | 1033 | { |
1034 | return iwm_write_mem(sc, addr, &val, 1); | | 1034 | return iwm_write_mem(sc, addr, &val, 1); |
1035 | } | | 1035 | } |
1036 | | | 1036 | |
1037 | static int | | 1037 | static int |
1038 | iwm_poll_bit(struct iwm_softc *sc, int reg, uint32_t bits, uint32_t mask, | | 1038 | iwm_poll_bit(struct iwm_softc *sc, int reg, uint32_t bits, uint32_t mask, |
1039 | int timo) | | 1039 | int timo) |
1040 | { | | 1040 | { |
1041 | for (;;) { | | 1041 | for (;;) { |
1042 | if ((IWM_READ(sc, reg) & mask) == (bits & mask)) { | | 1042 | if ((IWM_READ(sc, reg) & mask) == (bits & mask)) { |
1043 | return 1; | | 1043 | return 1; |
1044 | } | | 1044 | } |
1045 | if (timo < 10) { | | 1045 | if (timo < 10) { |
1046 | return 0; | | 1046 | return 0; |
1047 | } | | 1047 | } |
1048 | timo -= 10; | | 1048 | timo -= 10; |
1049 | DELAY(10); | | 1049 | DELAY(10); |
1050 | } | | 1050 | } |
1051 | } | | 1051 | } |
1052 | | | 1052 | |
1053 | static int | | 1053 | static int |
1054 | iwm_nic_lock(struct iwm_softc *sc) | | 1054 | iwm_nic_lock(struct iwm_softc *sc) |
1055 | { | | 1055 | { |
1056 | int rv = 0; | | 1056 | int rv = 0; |
1057 | | | 1057 | |
1058 | if (sc->sc_cmd_hold_nic_awake) | | 1058 | if (sc->sc_cmd_hold_nic_awake) |
1059 | return 1; | | 1059 | return 1; |
1060 | | | 1060 | |
1061 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, | | 1061 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, |
1062 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | | 1062 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); |
1063 | | | 1063 | |
1064 | if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) | | 1064 | if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) |
1065 | DELAY(2); | | 1065 | DELAY(2); |
1066 | | | 1066 | |
1067 | if (iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, | | 1067 | if (iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, |
1068 | IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, | | 1068 | IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, |
1069 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | | 1069 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
1070 | | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP, 15000)) { | | 1070 | | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP, 15000)) { |
1071 | rv = 1; | | 1071 | rv = 1; |
1072 | } else { | | 1072 | } else { |
1073 | DPRINTF(("%s: resetting device via NMI\n", DEVNAME(sc))); | | 1073 | DPRINTF(("%s: resetting device via NMI\n", DEVNAME(sc))); |
1074 | IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_FORCE_NMI); | | 1074 | IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_FORCE_NMI); |
1075 | } | | 1075 | } |
1076 | | | 1076 | |
1077 | return rv; | | 1077 | return rv; |
1078 | } | | 1078 | } |
1079 | | | 1079 | |
1080 | static void | | 1080 | static void |
1081 | iwm_nic_unlock(struct iwm_softc *sc) | | 1081 | iwm_nic_unlock(struct iwm_softc *sc) |
1082 | { | | 1082 | { |
1083 | | | 1083 | |
1084 | if (sc->sc_cmd_hold_nic_awake) | | 1084 | if (sc->sc_cmd_hold_nic_awake) |
1085 | return; | | 1085 | return; |
1086 | | | 1086 | |
1087 | IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, | | 1087 | IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, |
1088 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | | 1088 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); |
1089 | } | | 1089 | } |
1090 | | | 1090 | |
1091 | static void | | 1091 | static void |
1092 | iwm_set_bits_mask_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits, | | 1092 | iwm_set_bits_mask_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits, |
1093 | uint32_t mask) | | 1093 | uint32_t mask) |
1094 | { | | 1094 | { |
1095 | uint32_t val; | | 1095 | uint32_t val; |
1096 | | | 1096 | |
1097 | /* XXX: no error path? */ | | 1097 | /* XXX: no error path? */ |
1098 | if (iwm_nic_lock(sc)) { | | 1098 | if (iwm_nic_lock(sc)) { |
1099 | val = iwm_read_prph(sc, reg) & mask; | | 1099 | val = iwm_read_prph(sc, reg) & mask; |
1100 | val |= bits; | | 1100 | val |= bits; |
1101 | iwm_write_prph(sc, reg, val); | | 1101 | iwm_write_prph(sc, reg, val); |
1102 | iwm_nic_unlock(sc); | | 1102 | iwm_nic_unlock(sc); |
1103 | } | | 1103 | } |
1104 | } | | 1104 | } |
1105 | | | 1105 | |
1106 | static void | | 1106 | static void |
1107 | iwm_set_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) | | 1107 | iwm_set_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) |
1108 | { | | 1108 | { |
| @@ -6733,1574 +6733,1576 @@ iwm_init_hw(struct iwm_softc *sc) | | | @@ -6733,1574 +6733,1576 @@ iwm_init_hw(struct iwm_softc *sc) |
6733 | iwm_stop_device(sc); | | 6733 | iwm_stop_device(sc); |
6734 | return err; | | 6734 | return err; |
6735 | } | | 6735 | } |
6736 | | | 6736 | |
6737 | /* Allow multicast from our BSSID. */ | | 6737 | /* Allow multicast from our BSSID. */ |
6738 | static int | | 6738 | static int |
6739 | iwm_allow_mcast(struct iwm_softc *sc) | | 6739 | iwm_allow_mcast(struct iwm_softc *sc) |
6740 | { | | 6740 | { |
6741 | struct ieee80211com *ic = &sc->sc_ic; | | 6741 | struct ieee80211com *ic = &sc->sc_ic; |
6742 | struct ieee80211_node *ni = ic->ic_bss; | | 6742 | struct ieee80211_node *ni = ic->ic_bss; |
6743 | struct iwm_mcast_filter_cmd *cmd; | | 6743 | struct iwm_mcast_filter_cmd *cmd; |
6744 | size_t size; | | 6744 | size_t size; |
6745 | int err; | | 6745 | int err; |
6746 | | | 6746 | |
6747 | size = roundup(sizeof(*cmd), 4); | | 6747 | size = roundup(sizeof(*cmd), 4); |
6748 | cmd = kmem_intr_zalloc(size, KM_NOSLEEP); | | 6748 | cmd = kmem_intr_zalloc(size, KM_NOSLEEP); |
6749 | if (cmd == NULL) | | 6749 | if (cmd == NULL) |
6750 | return ENOMEM; | | 6750 | return ENOMEM; |
6751 | cmd->filter_own = 1; | | 6751 | cmd->filter_own = 1; |
6752 | cmd->port_id = 0; | | 6752 | cmd->port_id = 0; |
6753 | cmd->count = 0; | | 6753 | cmd->count = 0; |
6754 | cmd->pass_all = 1; | | 6754 | cmd->pass_all = 1; |
6755 | IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid); | | 6755 | IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid); |
6756 | | | 6756 | |
6757 | err = iwm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD, 0, size, cmd); | | 6757 | err = iwm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD, 0, size, cmd); |
6758 | kmem_intr_free(cmd, size); | | 6758 | kmem_intr_free(cmd, size); |
6759 | return err; | | 6759 | return err; |
6760 | } | | 6760 | } |
6761 | | | 6761 | |
6762 | static int | | 6762 | static int |
6763 | iwm_init(struct ifnet *ifp) | | 6763 | iwm_init(struct ifnet *ifp) |
6764 | { | | 6764 | { |
6765 | struct iwm_softc *sc = ifp->if_softc; | | 6765 | struct iwm_softc *sc = ifp->if_softc; |
6766 | int err; | | 6766 | int err; |
6767 | | | 6767 | |
6768 | if (ISSET(sc->sc_flags, IWM_FLAG_HW_INITED)) | | 6768 | if (ISSET(sc->sc_flags, IWM_FLAG_HW_INITED)) |
6769 | return 0; | | 6769 | return 0; |
6770 | | | 6770 | |
6771 | sc->sc_generation++; | | 6771 | sc->sc_generation++; |
6772 | sc->sc_flags &= ~IWM_FLAG_STOPPED; | | 6772 | sc->sc_flags &= ~IWM_FLAG_STOPPED; |
6773 | | | 6773 | |
6774 | err = iwm_init_hw(sc); | | 6774 | err = iwm_init_hw(sc); |
6775 | if (err) { | | 6775 | if (err) { |
6776 | iwm_stop(ifp, 1); | | 6776 | iwm_stop(ifp, 1); |
6777 | return err; | | 6777 | return err; |
6778 | } | | 6778 | } |
6779 | | | 6779 | |
6780 | ifp->if_flags &= ~IFF_OACTIVE; | | 6780 | ifp->if_flags &= ~IFF_OACTIVE; |
6781 | ifp->if_flags |= IFF_RUNNING; | | 6781 | ifp->if_flags |= IFF_RUNNING; |
6782 | | | 6782 | |
6783 | ieee80211_begin_scan(&sc->sc_ic, 0); | | 6783 | ieee80211_begin_scan(&sc->sc_ic, 0); |
6784 | SET(sc->sc_flags, IWM_FLAG_HW_INITED); | | 6784 | SET(sc->sc_flags, IWM_FLAG_HW_INITED); |
6785 | | | 6785 | |
6786 | return 0; | | 6786 | return 0; |
6787 | } | | 6787 | } |
6788 | | | 6788 | |
6789 | static void | | 6789 | static void |
6790 | iwm_start(struct ifnet *ifp) | | 6790 | iwm_start(struct ifnet *ifp) |
6791 | { | | 6791 | { |
6792 | struct iwm_softc *sc = ifp->if_softc; | | 6792 | struct iwm_softc *sc = ifp->if_softc; |
6793 | struct ieee80211com *ic = &sc->sc_ic; | | 6793 | struct ieee80211com *ic = &sc->sc_ic; |
6794 | struct ieee80211_node *ni; | | 6794 | struct ieee80211_node *ni; |
6795 | struct ether_header *eh; | | 6795 | struct ether_header *eh; |
6796 | struct mbuf *m; | | 6796 | struct mbuf *m; |
6797 | int ac; | | 6797 | int ac; |
6798 | | | 6798 | |
6799 | if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) | | 6799 | if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) |
6800 | return; | | 6800 | return; |
6801 | | | 6801 | |
6802 | for (;;) { | | 6802 | for (;;) { |
6803 | /* why isn't this done per-queue? */ | | 6803 | /* why isn't this done per-queue? */ |
6804 | if (sc->qfullmsk != 0) { | | 6804 | if (sc->qfullmsk != 0) { |
6805 | ifp->if_flags |= IFF_OACTIVE; | | 6805 | ifp->if_flags |= IFF_OACTIVE; |
6806 | break; | | 6806 | break; |
6807 | } | | 6807 | } |
6808 | | | 6808 | |
6809 | /* need to send management frames even if we're not RUNning */ | | 6809 | /* need to send management frames even if we're not RUNning */ |
6810 | IF_DEQUEUE(&ic->ic_mgtq, m); | | 6810 | IF_DEQUEUE(&ic->ic_mgtq, m); |
6811 | if (m) { | | 6811 | if (m) { |
6812 | ni = M_GETCTX(m, struct ieee80211_node *); | | 6812 | ni = M_GETCTX(m, struct ieee80211_node *); |
6813 | M_CLEARCTX(m); | | 6813 | M_CLEARCTX(m); |
6814 | ac = WME_AC_BE; | | 6814 | ac = WME_AC_BE; |
6815 | goto sendit; | | 6815 | goto sendit; |
6816 | } | | 6816 | } |
6817 | if (ic->ic_state != IEEE80211_S_RUN) { | | 6817 | if (ic->ic_state != IEEE80211_S_RUN) { |
6818 | break; | | 6818 | break; |
6819 | } | | 6819 | } |
6820 | | | 6820 | |
6821 | IFQ_DEQUEUE(&ifp->if_snd, m); | | 6821 | IFQ_DEQUEUE(&ifp->if_snd, m); |
6822 | if (m == NULL) | | 6822 | if (m == NULL) |
6823 | break; | | 6823 | break; |
6824 | | | 6824 | |
6825 | if (m->m_len < sizeof (*eh) && | | 6825 | if (m->m_len < sizeof (*eh) && |
6826 | (m = m_pullup(m, sizeof (*eh))) == NULL) { | | 6826 | (m = m_pullup(m, sizeof (*eh))) == NULL) { |
6827 | ifp->if_oerrors++; | | 6827 | ifp->if_oerrors++; |
6828 | continue; | | 6828 | continue; |
6829 | } | | 6829 | } |
6830 | | | 6830 | |
6831 | eh = mtod(m, struct ether_header *); | | 6831 | eh = mtod(m, struct ether_header *); |
6832 | ni = ieee80211_find_txnode(ic, eh->ether_dhost); | | 6832 | ni = ieee80211_find_txnode(ic, eh->ether_dhost); |
6833 | if (ni == NULL) { | | 6833 | if (ni == NULL) { |
6834 | m_freem(m); | | 6834 | m_freem(m); |
6835 | ifp->if_oerrors++; | | 6835 | ifp->if_oerrors++; |
6836 | continue; | | 6836 | continue; |
6837 | } | | 6837 | } |
6838 | | | 6838 | |
6839 | /* classify mbuf so we can find which tx ring to use */ | | 6839 | /* classify mbuf so we can find which tx ring to use */ |
6840 | if (ieee80211_classify(ic, m, ni) != 0) { | | 6840 | if (ieee80211_classify(ic, m, ni) != 0) { |
6841 | m_freem(m); | | 6841 | m_freem(m); |
6842 | ieee80211_free_node(ni); | | 6842 | ieee80211_free_node(ni); |
6843 | ifp->if_oerrors++; | | 6843 | ifp->if_oerrors++; |
6844 | continue; | | 6844 | continue; |
6845 | } | | 6845 | } |
6846 | | | 6846 | |
6847 | /* No QoS encapsulation for EAPOL frames. */ | | 6847 | /* No QoS encapsulation for EAPOL frames. */ |
6848 | ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? | | 6848 | ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? |
6849 | M_WME_GETAC(m) : WME_AC_BE; | | 6849 | M_WME_GETAC(m) : WME_AC_BE; |
6850 | | | 6850 | |
6851 | bpf_mtap(ifp, m); | | 6851 | bpf_mtap(ifp, m); |
6852 | | | 6852 | |
6853 | if ((m = ieee80211_encap(ic, m, ni)) == NULL) { | | 6853 | if ((m = ieee80211_encap(ic, m, ni)) == NULL) { |
6854 | ieee80211_free_node(ni); | | 6854 | ieee80211_free_node(ni); |
6855 | ifp->if_oerrors++; | | 6855 | ifp->if_oerrors++; |
6856 | continue; | | 6856 | continue; |
6857 | } | | 6857 | } |
6858 | | | 6858 | |
6859 | sendit: | | 6859 | sendit: |
6860 | bpf_mtap3(ic->ic_rawbpf, m); | | 6860 | bpf_mtap3(ic->ic_rawbpf, m); |
6861 | | | 6861 | |
6862 | if (iwm_tx(sc, m, ni, ac) != 0) { | | 6862 | if (iwm_tx(sc, m, ni, ac) != 0) { |
6863 | ieee80211_free_node(ni); | | 6863 | ieee80211_free_node(ni); |
6864 | ifp->if_oerrors++; | | 6864 | ifp->if_oerrors++; |
6865 | continue; | | 6865 | continue; |
6866 | } | | 6866 | } |
6867 | | | 6867 | |
6868 | if (ifp->if_flags & IFF_UP) { | | 6868 | if (ifp->if_flags & IFF_UP) { |
6869 | sc->sc_tx_timer = 15; | | 6869 | sc->sc_tx_timer = 15; |
6870 | ifp->if_timer = 1; | | 6870 | ifp->if_timer = 1; |
6871 | } | | 6871 | } |
6872 | } | | 6872 | } |
6873 | } | | 6873 | } |
6874 | | | 6874 | |
6875 | static void | | 6875 | static void |
6876 | iwm_stop(struct ifnet *ifp, int disable) | | 6876 | iwm_stop(struct ifnet *ifp, int disable) |
6877 | { | | 6877 | { |
6878 | struct iwm_softc *sc = ifp->if_softc; | | 6878 | struct iwm_softc *sc = ifp->if_softc; |
6879 | struct ieee80211com *ic = &sc->sc_ic; | | 6879 | struct ieee80211com *ic = &sc->sc_ic; |
6880 | struct iwm_node *in = (struct iwm_node *)ic->ic_bss; | | 6880 | struct iwm_node *in = (struct iwm_node *)ic->ic_bss; |
6881 | | | 6881 | |
6882 | sc->sc_flags &= ~IWM_FLAG_HW_INITED; | | 6882 | sc->sc_flags &= ~IWM_FLAG_HW_INITED; |
6883 | sc->sc_flags |= IWM_FLAG_STOPPED; | | 6883 | sc->sc_flags |= IWM_FLAG_STOPPED; |
6884 | sc->sc_generation++; | | 6884 | sc->sc_generation++; |
6885 | ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); | | 6885 | ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); |
6886 | | | 6886 | |
6887 | if (in) | | 6887 | if (in) |
6888 | in->in_phyctxt = NULL; | | 6888 | in->in_phyctxt = NULL; |
6889 | | | 6889 | |
6890 | if (ic->ic_state != IEEE80211_S_INIT) | | 6890 | if (ic->ic_state != IEEE80211_S_INIT) |
6891 | ieee80211_new_state(ic, IEEE80211_S_INIT, -1); | | 6891 | ieee80211_new_state(ic, IEEE80211_S_INIT, -1); |
6892 | | | 6892 | |
6893 | callout_stop(&sc->sc_calib_to); | | 6893 | callout_stop(&sc->sc_calib_to); |
6894 | iwm_led_blink_stop(sc); | | 6894 | iwm_led_blink_stop(sc); |
6895 | ifp->if_timer = sc->sc_tx_timer = 0; | | 6895 | ifp->if_timer = sc->sc_tx_timer = 0; |
6896 | iwm_stop_device(sc); | | 6896 | iwm_stop_device(sc); |
6897 | } | | 6897 | } |
6898 | | | 6898 | |
6899 | static void | | 6899 | static void |
6900 | iwm_watchdog(struct ifnet *ifp) | | 6900 | iwm_watchdog(struct ifnet *ifp) |
6901 | { | | 6901 | { |
6902 | struct iwm_softc *sc = ifp->if_softc; | | 6902 | struct iwm_softc *sc = ifp->if_softc; |
6903 | | | 6903 | |
6904 | ifp->if_timer = 0; | | 6904 | ifp->if_timer = 0; |
6905 | if (sc->sc_tx_timer > 0) { | | 6905 | if (sc->sc_tx_timer > 0) { |
6906 | if (--sc->sc_tx_timer == 0) { | | 6906 | if (--sc->sc_tx_timer == 0) { |
6907 | aprint_error_dev(sc->sc_dev, "device timeout\n"); | | 6907 | aprint_error_dev(sc->sc_dev, "device timeout\n"); |
6908 | #ifdef IWM_DEBUG | | 6908 | #ifdef IWM_DEBUG |
6909 | iwm_nic_error(sc); | | 6909 | iwm_nic_error(sc); |
6910 | #endif | | 6910 | #endif |
6911 | ifp->if_flags &= ~IFF_UP; | | 6911 | ifp->if_flags &= ~IFF_UP; |
6912 | iwm_stop(ifp, 1); | | 6912 | iwm_stop(ifp, 1); |
6913 | ifp->if_oerrors++; | | 6913 | ifp->if_oerrors++; |
6914 | return; | | 6914 | return; |
6915 | } | | 6915 | } |
6916 | ifp->if_timer = 1; | | 6916 | ifp->if_timer = 1; |
6917 | } | | 6917 | } |
6918 | | | 6918 | |
6919 | ieee80211_watchdog(&sc->sc_ic); | | 6919 | ieee80211_watchdog(&sc->sc_ic); |
6920 | } | | 6920 | } |
6921 | | | 6921 | |
6922 | static int | | 6922 | static int |
6923 | iwm_ioctl(struct ifnet *ifp, u_long cmd, void *data) | | 6923 | iwm_ioctl(struct ifnet *ifp, u_long cmd, void *data) |
6924 | { | | 6924 | { |
6925 | struct iwm_softc *sc = ifp->if_softc; | | 6925 | struct iwm_softc *sc = ifp->if_softc; |
6926 | struct ieee80211com *ic = &sc->sc_ic; | | 6926 | struct ieee80211com *ic = &sc->sc_ic; |
6927 | const struct sockaddr *sa; | | 6927 | const struct sockaddr *sa; |
6928 | int s, err = 0; | | 6928 | int s, err = 0; |
6929 | | | 6929 | |
6930 | s = splnet(); | | 6930 | s = splnet(); |
6931 | | | 6931 | |
6932 | switch (cmd) { | | 6932 | switch (cmd) { |
6933 | case SIOCSIFADDR: | | 6933 | case SIOCSIFADDR: |
6934 | ifp->if_flags |= IFF_UP; | | 6934 | ifp->if_flags |= IFF_UP; |
6935 | /* FALLTHROUGH */ | | 6935 | /* FALLTHROUGH */ |
6936 | case SIOCSIFFLAGS: | | 6936 | case SIOCSIFFLAGS: |
6937 | err = ifioctl_common(ifp, cmd, data); | | 6937 | err = ifioctl_common(ifp, cmd, data); |
6938 | if (err) | | 6938 | if (err) |
6939 | break; | | 6939 | break; |
6940 | if (ifp->if_flags & IFF_UP) { | | 6940 | if (ifp->if_flags & IFF_UP) { |
6941 | if (!(ifp->if_flags & IFF_RUNNING)) { | | 6941 | if (!(ifp->if_flags & IFF_RUNNING)) { |
6942 | err = iwm_init(ifp); | | 6942 | err = iwm_init(ifp); |
6943 | if (err) | | 6943 | if (err) |
6944 | ifp->if_flags &= ~IFF_UP; | | 6944 | ifp->if_flags &= ~IFF_UP; |
6945 | } | | 6945 | } |
6946 | } else { | | 6946 | } else { |
6947 | if (ifp->if_flags & IFF_RUNNING) | | 6947 | if (ifp->if_flags & IFF_RUNNING) |
6948 | iwm_stop(ifp, 1); | | 6948 | iwm_stop(ifp, 1); |
6949 | } | | 6949 | } |
6950 | break; | | 6950 | break; |
6951 | | | 6951 | |
6952 | case SIOCADDMULTI: | | 6952 | case SIOCADDMULTI: |
6953 | case SIOCDELMULTI: | | 6953 | case SIOCDELMULTI: |
6954 | if (!ISSET(sc->sc_flags, IWM_FLAG_ATTACHED)) { | | 6954 | if (!ISSET(sc->sc_flags, IWM_FLAG_ATTACHED)) { |
6955 | err = ENXIO; | | 6955 | err = ENXIO; |
6956 | break; | | 6956 | break; |
6957 | } | | 6957 | } |
6958 | sa = ifreq_getaddr(SIOCADDMULTI, (struct ifreq *)data); | | 6958 | sa = ifreq_getaddr(SIOCADDMULTI, (struct ifreq *)data); |
6959 | err = (cmd == SIOCADDMULTI) ? | | 6959 | err = (cmd == SIOCADDMULTI) ? |
6960 | ether_addmulti(sa, &sc->sc_ec) : | | 6960 | ether_addmulti(sa, &sc->sc_ec) : |
6961 | ether_delmulti(sa, &sc->sc_ec); | | 6961 | ether_delmulti(sa, &sc->sc_ec); |
6962 | if (err == ENETRESET) | | 6962 | if (err == ENETRESET) |
6963 | err = 0; | | 6963 | err = 0; |
6964 | break; | | 6964 | break; |
6965 | | | 6965 | |
6966 | default: | | 6966 | default: |
6967 | if (!ISSET(sc->sc_flags, IWM_FLAG_ATTACHED)) { | | 6967 | if (!ISSET(sc->sc_flags, IWM_FLAG_ATTACHED)) { |
6968 | err = ether_ioctl(ifp, cmd, data); | | 6968 | err = ether_ioctl(ifp, cmd, data); |
6969 | break; | | 6969 | break; |
6970 | } | | 6970 | } |
6971 | err = ieee80211_ioctl(ic, cmd, data); | | 6971 | err = ieee80211_ioctl(ic, cmd, data); |
6972 | break; | | 6972 | break; |
6973 | } | | 6973 | } |
6974 | | | 6974 | |
6975 | if (err == ENETRESET) { | | 6975 | if (err == ENETRESET) { |
6976 | err = 0; | | 6976 | err = 0; |
6977 | if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == | | 6977 | if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == |
6978 | (IFF_UP | IFF_RUNNING)) { | | 6978 | (IFF_UP | IFF_RUNNING)) { |
6979 | iwm_stop(ifp, 0); | | 6979 | iwm_stop(ifp, 0); |
6980 | err = iwm_init(ifp); | | 6980 | err = iwm_init(ifp); |
6981 | } | | 6981 | } |
6982 | } | | 6982 | } |
6983 | | | 6983 | |
6984 | splx(s); | | 6984 | splx(s); |
6985 | return err; | | 6985 | return err; |
6986 | } | | 6986 | } |
6987 | | | 6987 | |
6988 | /* | | 6988 | /* |
6989 | * Note: This structure is read from the device with IO accesses, | | 6989 | * Note: This structure is read from the device with IO accesses, |
6990 | * and the reading already does the endian conversion. As it is | | 6990 | * and the reading already does the endian conversion. As it is |
6991 | * read with uint32_t-sized accesses, any members with a different size | | 6991 | * read with uint32_t-sized accesses, any members with a different size |
6992 | * need to be ordered correctly though! | | 6992 | * need to be ordered correctly though! |
6993 | */ | | 6993 | */ |
6994 | struct iwm_error_event_table { | | 6994 | struct iwm_error_event_table { |
6995 | uint32_t valid; /* (nonzero) valid, (0) log is empty */ | | 6995 | uint32_t valid; /* (nonzero) valid, (0) log is empty */ |
6996 | uint32_t error_id; /* type of error */ | | 6996 | uint32_t error_id; /* type of error */ |
6997 | uint32_t trm_hw_status0; /* TRM HW status */ | | 6997 | uint32_t trm_hw_status0; /* TRM HW status */ |
6998 | uint32_t trm_hw_status1; /* TRM HW status */ | | 6998 | uint32_t trm_hw_status1; /* TRM HW status */ |
6999 | uint32_t blink2; /* branch link */ | | 6999 | uint32_t blink2; /* branch link */ |
7000 | uint32_t ilink1; /* interrupt link */ | | 7000 | uint32_t ilink1; /* interrupt link */ |
7001 | uint32_t ilink2; /* interrupt link */ | | 7001 | uint32_t ilink2; /* interrupt link */ |
7002 | uint32_t data1; /* error-specific data */ | | 7002 | uint32_t data1; /* error-specific data */ |
7003 | uint32_t data2; /* error-specific data */ | | 7003 | uint32_t data2; /* error-specific data */ |
7004 | uint32_t data3; /* error-specific data */ | | 7004 | uint32_t data3; /* error-specific data */ |
7005 | uint32_t bcon_time; /* beacon timer */ | | 7005 | uint32_t bcon_time; /* beacon timer */ |
7006 | uint32_t tsf_low; /* network timestamp function timer */ | | 7006 | uint32_t tsf_low; /* network timestamp function timer */ |
7007 | uint32_t tsf_hi; /* network timestamp function timer */ | | 7007 | uint32_t tsf_hi; /* network timestamp function timer */ |
7008 | uint32_t gp1; /* GP1 timer register */ | | 7008 | uint32_t gp1; /* GP1 timer register */ |
7009 | uint32_t gp2; /* GP2 timer register */ | | 7009 | uint32_t gp2; /* GP2 timer register */ |
7010 | uint32_t fw_rev_type; /* firmware revision type */ | | 7010 | uint32_t fw_rev_type; /* firmware revision type */ |
7011 | uint32_t major; /* uCode version major */ | | 7011 | uint32_t major; /* uCode version major */ |
7012 | uint32_t minor; /* uCode version minor */ | | 7012 | uint32_t minor; /* uCode version minor */ |
7013 | uint32_t hw_ver; /* HW Silicon version */ | | 7013 | uint32_t hw_ver; /* HW Silicon version */ |
7014 | uint32_t brd_ver; /* HW board version */ | | 7014 | uint32_t brd_ver; /* HW board version */ |
7015 | uint32_t log_pc; /* log program counter */ | | 7015 | uint32_t log_pc; /* log program counter */ |
7016 | uint32_t frame_ptr; /* frame pointer */ | | 7016 | uint32_t frame_ptr; /* frame pointer */ |
7017 | uint32_t stack_ptr; /* stack pointer */ | | 7017 | uint32_t stack_ptr; /* stack pointer */ |
7018 | uint32_t hcmd; /* last host command header */ | | 7018 | uint32_t hcmd; /* last host command header */ |
7019 | uint32_t isr0; /* isr status register LMPM_NIC_ISR0: | | 7019 | uint32_t isr0; /* isr status register LMPM_NIC_ISR0: |
7020 | * rxtx_flag */ | | 7020 | * rxtx_flag */ |
7021 | uint32_t isr1; /* isr status register LMPM_NIC_ISR1: | | 7021 | uint32_t isr1; /* isr status register LMPM_NIC_ISR1: |
7022 | * host_flag */ | | 7022 | * host_flag */ |
7023 | uint32_t isr2; /* isr status register LMPM_NIC_ISR2: | | 7023 | uint32_t isr2; /* isr status register LMPM_NIC_ISR2: |
7024 | * enc_flag */ | | 7024 | * enc_flag */ |
7025 | uint32_t isr3; /* isr status register LMPM_NIC_ISR3: | | 7025 | uint32_t isr3; /* isr status register LMPM_NIC_ISR3: |
7026 | * time_flag */ | | 7026 | * time_flag */ |
7027 | uint32_t isr4; /* isr status register LMPM_NIC_ISR4: | | 7027 | uint32_t isr4; /* isr status register LMPM_NIC_ISR4: |
7028 | * wico interrupt */ | | 7028 | * wico interrupt */ |
7029 | uint32_t last_cmd_id; /* last HCMD id handled by the firmware */ | | 7029 | uint32_t last_cmd_id; /* last HCMD id handled by the firmware */ |
7030 | uint32_t wait_event; /* wait event() caller address */ | | 7030 | uint32_t wait_event; /* wait event() caller address */ |
7031 | uint32_t l2p_control; /* L2pControlField */ | | 7031 | uint32_t l2p_control; /* L2pControlField */ |
7032 | uint32_t l2p_duration; /* L2pDurationField */ | | 7032 | uint32_t l2p_duration; /* L2pDurationField */ |
7033 | uint32_t l2p_mhvalid; /* L2pMhValidBits */ | | 7033 | uint32_t l2p_mhvalid; /* L2pMhValidBits */ |
7034 | uint32_t l2p_addr_match; /* L2pAddrMatchStat */ | | 7034 | uint32_t l2p_addr_match; /* L2pAddrMatchStat */ |
7035 | uint32_t lmpm_pmg_sel; /* indicate which clocks are turned on | | 7035 | uint32_t lmpm_pmg_sel; /* indicate which clocks are turned on |
7036 | * (LMPM_PMG_SEL) */ | | 7036 | * (LMPM_PMG_SEL) */ |
7037 | uint32_t u_timestamp; /* indicate when the date and time of the | | 7037 | uint32_t u_timestamp; /* indicate when the date and time of the |
7038 | * compilation */ | | 7038 | * compilation */ |
7039 | uint32_t flow_handler; /* FH read/write pointers, RX credit */ | | 7039 | uint32_t flow_handler; /* FH read/write pointers, RX credit */ |
7040 | } __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; | | 7040 | } __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; |
7041 | | | 7041 | |
7042 | /* | | 7042 | /* |
7043 | * UMAC error struct - relevant starting from family 8000 chip. | | 7043 | * UMAC error struct - relevant starting from family 8000 chip. |
7044 | * Note: This structure is read from the device with IO accesses, | | 7044 | * Note: This structure is read from the device with IO accesses, |
7045 | * and the reading already does the endian conversion. As it is | | 7045 | * and the reading already does the endian conversion. As it is |
7046 | * read with u32-sized accesses, any members with a different size | | 7046 | * read with u32-sized accesses, any members with a different size |
7047 | * need to be ordered correctly though! | | 7047 | * need to be ordered correctly though! |
7048 | */ | | 7048 | */ |
7049 | struct iwm_umac_error_event_table { | | 7049 | struct iwm_umac_error_event_table { |
7050 | uint32_t valid; /* (nonzero) valid, (0) log is empty */ | | 7050 | uint32_t valid; /* (nonzero) valid, (0) log is empty */ |
7051 | uint32_t error_id; /* type of error */ | | 7051 | uint32_t error_id; /* type of error */ |
7052 | uint32_t blink1; /* branch link */ | | 7052 | uint32_t blink1; /* branch link */ |
7053 | uint32_t blink2; /* branch link */ | | 7053 | uint32_t blink2; /* branch link */ |
7054 | uint32_t ilink1; /* interrupt link */ | | 7054 | uint32_t ilink1; /* interrupt link */ |
7055 | uint32_t ilink2; /* interrupt link */ | | 7055 | uint32_t ilink2; /* interrupt link */ |
7056 | uint32_t data1; /* error-specific data */ | | 7056 | uint32_t data1; /* error-specific data */ |
7057 | uint32_t data2; /* error-specific data */ | | 7057 | uint32_t data2; /* error-specific data */ |
7058 | uint32_t data3; /* error-specific data */ | | 7058 | uint32_t data3; /* error-specific data */ |
7059 | uint32_t umac_major; | | 7059 | uint32_t umac_major; |
7060 | uint32_t umac_minor; | | 7060 | uint32_t umac_minor; |
7061 | uint32_t frame_pointer; /* core register 27 */ | | 7061 | uint32_t frame_pointer; /* core register 27 */ |
7062 | uint32_t stack_pointer; /* core register 28 */ | | 7062 | uint32_t stack_pointer; /* core register 28 */ |
7063 | uint32_t cmd_header; /* latest host cmd sent to UMAC */ | | 7063 | uint32_t cmd_header; /* latest host cmd sent to UMAC */ |
7064 | uint32_t nic_isr_pref; /* ISR status register */ | | 7064 | uint32_t nic_isr_pref; /* ISR status register */ |
7065 | } __packed; | | 7065 | } __packed; |
7066 | | | 7066 | |
7067 | #define ERROR_START_OFFSET (1 * sizeof(uint32_t)) | | 7067 | #define ERROR_START_OFFSET (1 * sizeof(uint32_t)) |
7068 | #define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) | | 7068 | #define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) |
7069 | | | 7069 | |
7070 | #ifdef IWM_DEBUG | | 7070 | #ifdef IWM_DEBUG |
7071 | static const struct { | | 7071 | static const struct { |
7072 | const char *name; | | 7072 | const char *name; |
7073 | uint8_t num; | | 7073 | uint8_t num; |
7074 | } advanced_lookup[] = { | | 7074 | } advanced_lookup[] = { |
7075 | { "NMI_INTERRUPT_WDG", 0x34 }, | | 7075 | { "NMI_INTERRUPT_WDG", 0x34 }, |
7076 | { "SYSASSERT", 0x35 }, | | 7076 | { "SYSASSERT", 0x35 }, |
7077 | { "UCODE_VERSION_MISMATCH", 0x37 }, | | 7077 | { "UCODE_VERSION_MISMATCH", 0x37 }, |
7078 | { "BAD_COMMAND", 0x38 }, | | 7078 | { "BAD_COMMAND", 0x38 }, |
7079 | { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, | | 7079 | { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, |
7080 | { "FATAL_ERROR", 0x3D }, | | 7080 | { "FATAL_ERROR", 0x3D }, |
7081 | { "NMI_TRM_HW_ERR", 0x46 }, | | 7081 | { "NMI_TRM_HW_ERR", 0x46 }, |
7082 | { "NMI_INTERRUPT_TRM", 0x4C }, | | 7082 | { "NMI_INTERRUPT_TRM", 0x4C }, |
7083 | { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, | | 7083 | { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, |
7084 | { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, | | 7084 | { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, |
7085 | { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, | | 7085 | { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, |
7086 | { "NMI_INTERRUPT_HOST", 0x66 }, | | 7086 | { "NMI_INTERRUPT_HOST", 0x66 }, |
7087 | { "NMI_INTERRUPT_ACTION_PT", 0x7C }, | | 7087 | { "NMI_INTERRUPT_ACTION_PT", 0x7C }, |
7088 | { "NMI_INTERRUPT_UNKNOWN", 0x84 }, | | 7088 | { "NMI_INTERRUPT_UNKNOWN", 0x84 }, |
7089 | { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, | | 7089 | { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, |
7090 | { "ADVANCED_SYSASSERT", 0 }, | | 7090 | { "ADVANCED_SYSASSERT", 0 }, |
7091 | }; | | 7091 | }; |
7092 | | | 7092 | |
7093 | static const char * | | 7093 | static const char * |
7094 | iwm_desc_lookup(uint32_t num) | | 7094 | iwm_desc_lookup(uint32_t num) |
7095 | { | | 7095 | { |
7096 | int i; | | 7096 | int i; |
7097 | | | 7097 | |
7098 | for (i = 0; i < __arraycount(advanced_lookup) - 1; i++) | | 7098 | for (i = 0; i < __arraycount(advanced_lookup) - 1; i++) |
7099 | if (advanced_lookup[i].num == num) | | 7099 | if (advanced_lookup[i].num == num) |
7100 | return advanced_lookup[i].name; | | 7100 | return advanced_lookup[i].name; |
7101 | | | 7101 | |
7102 | /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ | | 7102 | /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ |
7103 | return advanced_lookup[i].name; | | 7103 | return advanced_lookup[i].name; |
7104 | } | | 7104 | } |
7105 | | | 7105 | |
7106 | /* | | 7106 | /* |
7107 | * Support for dumping the error log seemed like a good idea ... | | 7107 | * Support for dumping the error log seemed like a good idea ... |
7108 | * but it's mostly hex junk and the only sensible thing is the | | 7108 | * but it's mostly hex junk and the only sensible thing is the |
7109 | * hw/ucode revision (which we know anyway). Since it's here, | | 7109 | * hw/ucode revision (which we know anyway). Since it's here, |
7110 | * I'll just leave it in, just in case e.g. the Intel guys want to | | 7110 | * I'll just leave it in, just in case e.g. the Intel guys want to |
7111 | * help us decipher some "ADVANCED_SYSASSERT" later. | | 7111 | * help us decipher some "ADVANCED_SYSASSERT" later. |
7112 | */ | | 7112 | */ |
7113 | static void | | 7113 | static void |
7114 | iwm_nic_error(struct iwm_softc *sc) | | 7114 | iwm_nic_error(struct iwm_softc *sc) |
7115 | { | | 7115 | { |
7116 | struct iwm_error_event_table t; | | 7116 | struct iwm_error_event_table t; |
7117 | uint32_t base; | | 7117 | uint32_t base; |
7118 | | | 7118 | |
7119 | aprint_error_dev(sc->sc_dev, "dumping device error log\n"); | | 7119 | aprint_error_dev(sc->sc_dev, "dumping device error log\n"); |
7120 | base = sc->sc_uc.uc_error_event_table; | | 7120 | base = sc->sc_uc.uc_error_event_table; |
7121 | if (base < 0x800000) { | | 7121 | if (base < 0x800000) { |
7122 | aprint_error_dev(sc->sc_dev, | | 7122 | aprint_error_dev(sc->sc_dev, |
7123 | "Invalid error log pointer 0x%08x\n", base); | | 7123 | "Invalid error log pointer 0x%08x\n", base); |
7124 | return; | | 7124 | return; |
7125 | } | | 7125 | } |
7126 | | | 7126 | |
7127 | if (iwm_read_mem(sc, base, &t, sizeof(t)/sizeof(uint32_t))) { | | 7127 | if (iwm_read_mem(sc, base, &t, sizeof(t)/sizeof(uint32_t))) { |
7128 | aprint_error_dev(sc->sc_dev, "reading errlog failed\n"); | | 7128 | aprint_error_dev(sc->sc_dev, "reading errlog failed\n"); |
7129 | return; | | 7129 | return; |
7130 | } | | 7130 | } |
7131 | | | 7131 | |
7132 | if (!t.valid) { | | 7132 | if (!t.valid) { |
7133 | aprint_error_dev(sc->sc_dev, "errlog not found, skipping\n"); | | 7133 | aprint_error_dev(sc->sc_dev, "errlog not found, skipping\n"); |
7134 | return; | | 7134 | return; |
7135 | } | | 7135 | } |
7136 | | | 7136 | |
7137 | if (ERROR_START_OFFSET <= t.valid * ERROR_ELEM_SIZE) { | | 7137 | if (ERROR_START_OFFSET <= t.valid * ERROR_ELEM_SIZE) { |
7138 | aprint_error_dev(sc->sc_dev, "Start Error Log Dump:\n"); | | 7138 | aprint_error_dev(sc->sc_dev, "Start Error Log Dump:\n"); |
7139 | aprint_error_dev(sc->sc_dev, "Status: 0x%x, count: %d\n", | | 7139 | aprint_error_dev(sc->sc_dev, "Status: 0x%x, count: %d\n", |
7140 | sc->sc_flags, t.valid); | | 7140 | sc->sc_flags, t.valid); |
7141 | } | | 7141 | } |
7142 | | | 7142 | |
7143 | aprint_error_dev(sc->sc_dev, "%08X | %-28s\n", t.error_id, | | 7143 | aprint_error_dev(sc->sc_dev, "%08X | %-28s\n", t.error_id, |
7144 | iwm_desc_lookup(t.error_id)); | | 7144 | iwm_desc_lookup(t.error_id)); |
7145 | aprint_error_dev(sc->sc_dev, "%08X | trm_hw_status0\n", | | 7145 | aprint_error_dev(sc->sc_dev, "%08X | trm_hw_status0\n", |
7146 | t.trm_hw_status0); | | 7146 | t.trm_hw_status0); |
7147 | aprint_error_dev(sc->sc_dev, "%08X | trm_hw_status1\n", | | 7147 | aprint_error_dev(sc->sc_dev, "%08X | trm_hw_status1\n", |
7148 | t.trm_hw_status1); | | 7148 | t.trm_hw_status1); |
7149 | aprint_error_dev(sc->sc_dev, "%08X | branchlink2\n", t.blink2); | | 7149 | aprint_error_dev(sc->sc_dev, "%08X | branchlink2\n", t.blink2); |
7150 | aprint_error_dev(sc->sc_dev, "%08X | interruptlink1\n", t.ilink1); | | 7150 | aprint_error_dev(sc->sc_dev, "%08X | interruptlink1\n", t.ilink1); |
7151 | aprint_error_dev(sc->sc_dev, "%08X | interruptlink2\n", t.ilink2); | | 7151 | aprint_error_dev(sc->sc_dev, "%08X | interruptlink2\n", t.ilink2); |
7152 | aprint_error_dev(sc->sc_dev, "%08X | data1\n", t.data1); | | 7152 | aprint_error_dev(sc->sc_dev, "%08X | data1\n", t.data1); |
7153 | aprint_error_dev(sc->sc_dev, "%08X | data2\n", t.data2); | | 7153 | aprint_error_dev(sc->sc_dev, "%08X | data2\n", t.data2); |
7154 | aprint_error_dev(sc->sc_dev, "%08X | data3\n", t.data3); | | 7154 | aprint_error_dev(sc->sc_dev, "%08X | data3\n", t.data3); |
7155 | aprint_error_dev(sc->sc_dev, "%08X | beacon time\n", t.bcon_time); | | 7155 | aprint_error_dev(sc->sc_dev, "%08X | beacon time\n", t.bcon_time); |
7156 | aprint_error_dev(sc->sc_dev, "%08X | tsf low\n", t.tsf_low); | | 7156 | aprint_error_dev(sc->sc_dev, "%08X | tsf low\n", t.tsf_low); |
7157 | aprint_error_dev(sc->sc_dev, "%08X | tsf hi\n", t.tsf_hi); | | 7157 | aprint_error_dev(sc->sc_dev, "%08X | tsf hi\n", t.tsf_hi); |
7158 | aprint_error_dev(sc->sc_dev, "%08X | time gp1\n", t.gp1); | | 7158 | aprint_error_dev(sc->sc_dev, "%08X | time gp1\n", t.gp1); |
7159 | aprint_error_dev(sc->sc_dev, "%08X | time gp2\n", t.gp2); | | 7159 | aprint_error_dev(sc->sc_dev, "%08X | time gp2\n", t.gp2); |
7160 | aprint_error_dev(sc->sc_dev, "%08X | uCode revision type\n", | | 7160 | aprint_error_dev(sc->sc_dev, "%08X | uCode revision type\n", |
7161 | t.fw_rev_type); | | 7161 | t.fw_rev_type); |
7162 | aprint_error_dev(sc->sc_dev, "%08X | uCode version major\n", | | 7162 | aprint_error_dev(sc->sc_dev, "%08X | uCode version major\n", |
7163 | t.major); | | 7163 | t.major); |
7164 | aprint_error_dev(sc->sc_dev, "%08X | uCode version minor\n", | | 7164 | aprint_error_dev(sc->sc_dev, "%08X | uCode version minor\n", |
7165 | t.minor); | | 7165 | t.minor); |
7166 | aprint_error_dev(sc->sc_dev, "%08X | hw version\n", t.hw_ver); | | 7166 | aprint_error_dev(sc->sc_dev, "%08X | hw version\n", t.hw_ver); |
7167 | aprint_error_dev(sc->sc_dev, "%08X | board version\n", t.brd_ver); | | 7167 | aprint_error_dev(sc->sc_dev, "%08X | board version\n", t.brd_ver); |
7168 | aprint_error_dev(sc->sc_dev, "%08X | hcmd\n", t.hcmd); | | 7168 | aprint_error_dev(sc->sc_dev, "%08X | hcmd\n", t.hcmd); |
7169 | aprint_error_dev(sc->sc_dev, "%08X | isr0\n", t.isr0); | | 7169 | aprint_error_dev(sc->sc_dev, "%08X | isr0\n", t.isr0); |
7170 | aprint_error_dev(sc->sc_dev, "%08X | isr1\n", t.isr1); | | 7170 | aprint_error_dev(sc->sc_dev, "%08X | isr1\n", t.isr1); |
7171 | aprint_error_dev(sc->sc_dev, "%08X | isr2\n", t.isr2); | | 7171 | aprint_error_dev(sc->sc_dev, "%08X | isr2\n", t.isr2); |
7172 | aprint_error_dev(sc->sc_dev, "%08X | isr3\n", t.isr3); | | 7172 | aprint_error_dev(sc->sc_dev, "%08X | isr3\n", t.isr3); |
7173 | aprint_error_dev(sc->sc_dev, "%08X | isr4\n", t.isr4); | | 7173 | aprint_error_dev(sc->sc_dev, "%08X | isr4\n", t.isr4); |
7174 | aprint_error_dev(sc->sc_dev, "%08X | last cmd Id\n", t.last_cmd_id); | | 7174 | aprint_error_dev(sc->sc_dev, "%08X | last cmd Id\n", t.last_cmd_id); |
7175 | aprint_error_dev(sc->sc_dev, "%08X | wait_event\n", t.wait_event); | | 7175 | aprint_error_dev(sc->sc_dev, "%08X | wait_event\n", t.wait_event); |
7176 | aprint_error_dev(sc->sc_dev, "%08X | l2p_control\n", t.l2p_control); | | 7176 | aprint_error_dev(sc->sc_dev, "%08X | l2p_control\n", t.l2p_control); |
7177 | aprint_error_dev(sc->sc_dev, "%08X | l2p_duration\n", t.l2p_duration); | | 7177 | aprint_error_dev(sc->sc_dev, "%08X | l2p_duration\n", t.l2p_duration); |
7178 | aprint_error_dev(sc->sc_dev, "%08X | l2p_mhvalid\n", t.l2p_mhvalid); | | 7178 | aprint_error_dev(sc->sc_dev, "%08X | l2p_mhvalid\n", t.l2p_mhvalid); |
7179 | aprint_error_dev(sc->sc_dev, "%08X | l2p_addr_match\n", | | 7179 | aprint_error_dev(sc->sc_dev, "%08X | l2p_addr_match\n", |
7180 | t.l2p_addr_match); | | 7180 | t.l2p_addr_match); |
7181 | aprint_error_dev(sc->sc_dev, "%08X | lmpm_pmg_sel\n", t.lmpm_pmg_sel); | | 7181 | aprint_error_dev(sc->sc_dev, "%08X | lmpm_pmg_sel\n", t.lmpm_pmg_sel); |
7182 | aprint_error_dev(sc->sc_dev, "%08X | timestamp\n", t.u_timestamp); | | 7182 | aprint_error_dev(sc->sc_dev, "%08X | timestamp\n", t.u_timestamp); |
7183 | aprint_error_dev(sc->sc_dev, "%08X | flow_handler\n", t.flow_handler); | | 7183 | aprint_error_dev(sc->sc_dev, "%08X | flow_handler\n", t.flow_handler); |
7184 | | | 7184 | |
7185 | if (sc->sc_uc.uc_umac_error_event_table) | | 7185 | if (sc->sc_uc.uc_umac_error_event_table) |
7186 | iwm_nic_umac_error(sc); | | 7186 | iwm_nic_umac_error(sc); |
7187 | } | | 7187 | } |
7188 | | | 7188 | |
7189 | static void | | 7189 | static void |
7190 | iwm_nic_umac_error(struct iwm_softc *sc) | | 7190 | iwm_nic_umac_error(struct iwm_softc *sc) |
7191 | { | | 7191 | { |
7192 | struct iwm_umac_error_event_table t; | | 7192 | struct iwm_umac_error_event_table t; |
7193 | uint32_t base; | | 7193 | uint32_t base; |
7194 | | | 7194 | |
7195 | base = sc->sc_uc.uc_umac_error_event_table; | | 7195 | base = sc->sc_uc.uc_umac_error_event_table; |
7196 | | | 7196 | |
7197 | if (base < 0x800000) { | | 7197 | if (base < 0x800000) { |
7198 | aprint_error_dev(sc->sc_dev, | | 7198 | aprint_error_dev(sc->sc_dev, |
7199 | "Invalid error log pointer 0x%08x\n", base); | | 7199 | "Invalid error log pointer 0x%08x\n", base); |
7200 | return; | | 7200 | return; |
7201 | } | | 7201 | } |
7202 | | | 7202 | |
7203 | if (iwm_read_mem(sc, base, &t, sizeof(t)/sizeof(uint32_t))) { | | 7203 | if (iwm_read_mem(sc, base, &t, sizeof(t)/sizeof(uint32_t))) { |
7204 | aprint_error_dev(sc->sc_dev, "reading errlog failed\n"); | | 7204 | aprint_error_dev(sc->sc_dev, "reading errlog failed\n"); |
7205 | return; | | 7205 | return; |
7206 | } | | 7206 | } |
7207 | | | 7207 | |
7208 | if (ERROR_START_OFFSET <= t.valid * ERROR_ELEM_SIZE) { | | 7208 | if (ERROR_START_OFFSET <= t.valid * ERROR_ELEM_SIZE) { |
7209 | aprint_error_dev(sc->sc_dev, "Start UMAC Error Log Dump:\n"); | | 7209 | aprint_error_dev(sc->sc_dev, "Start UMAC Error Log Dump:\n"); |
7210 | aprint_error_dev(sc->sc_dev, "Status: 0x%x, count: %d\n", | | 7210 | aprint_error_dev(sc->sc_dev, "Status: 0x%x, count: %d\n", |
7211 | sc->sc_flags, t.valid); | | 7211 | sc->sc_flags, t.valid); |
7212 | } | | 7212 | } |
7213 | | | 7213 | |
7214 | aprint_error_dev(sc->sc_dev, "0x%08X | %s\n", t.error_id, | | 7214 | aprint_error_dev(sc->sc_dev, "0x%08X | %s\n", t.error_id, |
7215 | iwm_desc_lookup(t.error_id)); | | 7215 | iwm_desc_lookup(t.error_id)); |
7216 | aprint_error_dev(sc->sc_dev, "0x%08X | umac branchlink1\n", t.blink1); | | 7216 | aprint_error_dev(sc->sc_dev, "0x%08X | umac branchlink1\n", t.blink1); |
7217 | aprint_error_dev(sc->sc_dev, "0x%08X | umac branchlink2\n", t.blink2); | | 7217 | aprint_error_dev(sc->sc_dev, "0x%08X | umac branchlink2\n", t.blink2); |
7218 | aprint_error_dev(sc->sc_dev, "0x%08X | umac interruptlink1\n", | | 7218 | aprint_error_dev(sc->sc_dev, "0x%08X | umac interruptlink1\n", |
7219 | t.ilink1); | | 7219 | t.ilink1); |
7220 | aprint_error_dev(sc->sc_dev, "0x%08X | umac interruptlink2\n", | | 7220 | aprint_error_dev(sc->sc_dev, "0x%08X | umac interruptlink2\n", |
7221 | t.ilink2); | | 7221 | t.ilink2); |
7222 | aprint_error_dev(sc->sc_dev, "0x%08X | umac data1\n", t.data1); | | 7222 | aprint_error_dev(sc->sc_dev, "0x%08X | umac data1\n", t.data1); |
7223 | aprint_error_dev(sc->sc_dev, "0x%08X | umac data2\n", t.data2); | | 7223 | aprint_error_dev(sc->sc_dev, "0x%08X | umac data2\n", t.data2); |
7224 | aprint_error_dev(sc->sc_dev, "0x%08X | umac data3\n", t.data3); | | 7224 | aprint_error_dev(sc->sc_dev, "0x%08X | umac data3\n", t.data3); |
7225 | aprint_error_dev(sc->sc_dev, "0x%08X | umac major\n", t.umac_major); | | 7225 | aprint_error_dev(sc->sc_dev, "0x%08X | umac major\n", t.umac_major); |
7226 | aprint_error_dev(sc->sc_dev, "0x%08X | umac minor\n", t.umac_minor); | | 7226 | aprint_error_dev(sc->sc_dev, "0x%08X | umac minor\n", t.umac_minor); |
7227 | aprint_error_dev(sc->sc_dev, "0x%08X | frame pointer\n", | | 7227 | aprint_error_dev(sc->sc_dev, "0x%08X | frame pointer\n", |
7228 | t.frame_pointer); | | 7228 | t.frame_pointer); |
7229 | aprint_error_dev(sc->sc_dev, "0x%08X | stack pointer\n", | | 7229 | aprint_error_dev(sc->sc_dev, "0x%08X | stack pointer\n", |
7230 | t.stack_pointer); | | 7230 | t.stack_pointer); |
7231 | aprint_error_dev(sc->sc_dev, "0x%08X | last host cmd\n", t.cmd_header); | | 7231 | aprint_error_dev(sc->sc_dev, "0x%08X | last host cmd\n", t.cmd_header); |
7232 | aprint_error_dev(sc->sc_dev, "0x%08X | isr status reg\n", | | 7232 | aprint_error_dev(sc->sc_dev, "0x%08X | isr status reg\n", |
7233 | t.nic_isr_pref); | | 7233 | t.nic_isr_pref); |
7234 | } | | 7234 | } |
7235 | #endif | | 7235 | #endif |
7236 | | | 7236 | |
7237 | #define SYNC_RESP_STRUCT(_var_, _pkt_) \ | | 7237 | #define SYNC_RESP_STRUCT(_var_, _pkt_) \ |
7238 | do { \ | | 7238 | do { \ |
7239 | bus_dmamap_sync(sc->sc_dmat, data->map, sizeof(*(_pkt_)), \ | | 7239 | bus_dmamap_sync(sc->sc_dmat, data->map, sizeof(*(_pkt_)), \ |
7240 | sizeof(*(_var_)), BUS_DMASYNC_POSTREAD); \ | | 7240 | sizeof(*(_var_)), BUS_DMASYNC_POSTREAD); \ |
7241 | _var_ = (void *)((_pkt_)+1); \ | | 7241 | _var_ = (void *)((_pkt_)+1); \ |
7242 | } while (/*CONSTCOND*/0) | | 7242 | } while (/*CONSTCOND*/0) |
7243 | | | 7243 | |
7244 | #define SYNC_RESP_PTR(_ptr_, _len_, _pkt_) \ | | 7244 | #define SYNC_RESP_PTR(_ptr_, _len_, _pkt_) \ |
7245 | do { \ | | 7245 | do { \ |
7246 | bus_dmamap_sync(sc->sc_dmat, data->map, sizeof(*(_pkt_)), \ | | 7246 | bus_dmamap_sync(sc->sc_dmat, data->map, sizeof(*(_pkt_)), \ |
7247 | sizeof(len), BUS_DMASYNC_POSTREAD); \ | | 7247 | sizeof(len), BUS_DMASYNC_POSTREAD); \ |
7248 | _ptr_ = (void *)((_pkt_)+1); \ | | 7248 | _ptr_ = (void *)((_pkt_)+1); \ |
7249 | } while (/*CONSTCOND*/0) | | 7249 | } while (/*CONSTCOND*/0) |
7250 | | | 7250 | |
7251 | #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT); | | 7251 | #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT); |
7252 | | | 7252 | |
7253 | static void | | 7253 | static void |
7254 | iwm_notif_intr(struct iwm_softc *sc) | | 7254 | iwm_notif_intr(struct iwm_softc *sc) |
7255 | { | | 7255 | { |
7256 | uint16_t hw; | | 7256 | uint16_t hw; |
7257 | | | 7257 | |
7258 | bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map, | | 7258 | bus_dmamap_sync(sc->sc_dmat, sc->rxq.stat_dma.map, |
7259 | 0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD); | | 7259 | 0, sc->rxq.stat_dma.size, BUS_DMASYNC_POSTREAD); |
7260 | | | 7260 | |
7261 | hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; | | 7261 | hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; |
7262 | while (sc->rxq.cur != hw) { | | 7262 | while (sc->rxq.cur != hw) { |
7263 | struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; | | 7263 | struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; |
7264 | struct iwm_rx_packet *pkt; | | 7264 | struct iwm_rx_packet *pkt; |
7265 | struct iwm_cmd_response *cresp; | | 7265 | struct iwm_cmd_response *cresp; |
7266 | int orig_qid, qid, idx, code; | | 7266 | int orig_qid, qid, idx, code; |
7267 | | | 7267 | |
7268 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt), | | 7268 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt), |
7269 | BUS_DMASYNC_POSTREAD); | | 7269 | BUS_DMASYNC_POSTREAD); |
7270 | pkt = mtod(data->m, struct iwm_rx_packet *); | | 7270 | pkt = mtod(data->m, struct iwm_rx_packet *); |
7271 | | | 7271 | |
7272 | orig_qid = pkt->hdr.qid; | | 7272 | orig_qid = pkt->hdr.qid; |
7273 | qid = orig_qid & ~0x80; | | 7273 | qid = orig_qid & ~0x80; |
7274 | idx = pkt->hdr.idx; | | 7274 | idx = pkt->hdr.idx; |
7275 | | | 7275 | |
7276 | code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); | | 7276 | code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); |
7277 | | | 7277 | |
7278 | /* | | 7278 | /* |
7279 | * randomly get these from the firmware, no idea why. | | 7279 | * randomly get these from the firmware, no idea why. |
7280 | * they at least seem harmless, so just ignore them for now | | 7280 | * they at least seem harmless, so just ignore them for now |
7281 | */ | | 7281 | */ |
7282 | if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0) | | 7282 | if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0) |
7283 | || pkt->len_n_flags == htole32(0x55550000))) { | | 7283 | || pkt->len_n_flags == htole32(0x55550000))) { |
7284 | ADVANCE_RXQ(sc); | | 7284 | ADVANCE_RXQ(sc); |
7285 | continue; | | 7285 | continue; |
7286 | } | | 7286 | } |
7287 | | | 7287 | |
7288 | switch (code) { | | 7288 | switch (code) { |
7289 | case IWM_REPLY_RX_PHY_CMD: | | 7289 | case IWM_REPLY_RX_PHY_CMD: |
7290 | iwm_rx_rx_phy_cmd(sc, pkt, data); | | 7290 | iwm_rx_rx_phy_cmd(sc, pkt, data); |
7291 | break; | | 7291 | break; |
7292 | | | 7292 | |
7293 | case IWM_REPLY_RX_MPDU_CMD: | | 7293 | case IWM_REPLY_RX_MPDU_CMD: |
7294 | iwm_rx_rx_mpdu(sc, pkt, data); | | 7294 | iwm_rx_rx_mpdu(sc, pkt, data); |
7295 | break; | | 7295 | break; |
7296 | | | 7296 | |
7297 | case IWM_TX_CMD: | | 7297 | case IWM_TX_CMD: |
7298 | iwm_rx_tx_cmd(sc, pkt, data); | | 7298 | iwm_rx_tx_cmd(sc, pkt, data); |
7299 | break; | | 7299 | break; |
7300 | | | 7300 | |
7301 | case IWM_MISSED_BEACONS_NOTIFICATION: | | 7301 | case IWM_MISSED_BEACONS_NOTIFICATION: |
7302 | iwm_rx_missed_beacons_notif(sc, pkt, data); | | 7302 | iwm_rx_missed_beacons_notif(sc, pkt, data); |
7303 | break; | | 7303 | break; |
7304 | | | 7304 | |
7305 | case IWM_MFUART_LOAD_NOTIFICATION: | | 7305 | case IWM_MFUART_LOAD_NOTIFICATION: |
7306 | break; | | 7306 | break; |
7307 | | | 7307 | |
7308 | case IWM_ALIVE: { | | 7308 | case IWM_ALIVE: { |
7309 | struct iwm_alive_resp_v1 *resp1; | | 7309 | struct iwm_alive_resp_v1 *resp1; |
7310 | struct iwm_alive_resp_v2 *resp2; | | 7310 | struct iwm_alive_resp_v2 *resp2; |
7311 | struct iwm_alive_resp_v3 *resp3; | | 7311 | struct iwm_alive_resp_v3 *resp3; |
7312 | | | 7312 | |
7313 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) { | | 7313 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) { |
7314 | SYNC_RESP_STRUCT(resp1, pkt); | | 7314 | SYNC_RESP_STRUCT(resp1, pkt); |
7315 | sc->sc_uc.uc_error_event_table | | 7315 | sc->sc_uc.uc_error_event_table |
7316 | = le32toh(resp1->error_event_table_ptr); | | 7316 | = le32toh(resp1->error_event_table_ptr); |
7317 | sc->sc_uc.uc_log_event_table | | 7317 | sc->sc_uc.uc_log_event_table |
7318 | = le32toh(resp1->log_event_table_ptr); | | 7318 | = le32toh(resp1->log_event_table_ptr); |
7319 | sc->sched_base = le32toh(resp1->scd_base_ptr); | | 7319 | sc->sched_base = le32toh(resp1->scd_base_ptr); |
7320 | if (resp1->status == IWM_ALIVE_STATUS_OK) | | 7320 | if (resp1->status == IWM_ALIVE_STATUS_OK) |
7321 | sc->sc_uc.uc_ok = 1; | | 7321 | sc->sc_uc.uc_ok = 1; |
7322 | else | | 7322 | else |
7323 | sc->sc_uc.uc_ok = 0; | | 7323 | sc->sc_uc.uc_ok = 0; |
7324 | } | | 7324 | } |
7325 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) { | | 7325 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) { |
7326 | SYNC_RESP_STRUCT(resp2, pkt); | | 7326 | SYNC_RESP_STRUCT(resp2, pkt); |
7327 | sc->sc_uc.uc_error_event_table | | 7327 | sc->sc_uc.uc_error_event_table |
7328 | = le32toh(resp2->error_event_table_ptr); | | 7328 | = le32toh(resp2->error_event_table_ptr); |
7329 | sc->sc_uc.uc_log_event_table | | 7329 | sc->sc_uc.uc_log_event_table |
7330 | = le32toh(resp2->log_event_table_ptr); | | 7330 | = le32toh(resp2->log_event_table_ptr); |
7331 | sc->sched_base = le32toh(resp2->scd_base_ptr); | | 7331 | sc->sched_base = le32toh(resp2->scd_base_ptr); |
7332 | sc->sc_uc.uc_umac_error_event_table | | 7332 | sc->sc_uc.uc_umac_error_event_table |
7333 | = le32toh(resp2->error_info_addr); | | 7333 | = le32toh(resp2->error_info_addr); |
7334 | if (resp2->status == IWM_ALIVE_STATUS_OK) | | 7334 | if (resp2->status == IWM_ALIVE_STATUS_OK) |
7335 | sc->sc_uc.uc_ok = 1; | | 7335 | sc->sc_uc.uc_ok = 1; |
7336 | else | | 7336 | else |
7337 | sc->sc_uc.uc_ok = 0; | | 7337 | sc->sc_uc.uc_ok = 0; |
7338 | } | | 7338 | } |
7339 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) { | | 7339 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) { |
7340 | SYNC_RESP_STRUCT(resp3, pkt); | | 7340 | SYNC_RESP_STRUCT(resp3, pkt); |
7341 | sc->sc_uc.uc_error_event_table | | 7341 | sc->sc_uc.uc_error_event_table |
7342 | = le32toh(resp3->error_event_table_ptr); | | 7342 | = le32toh(resp3->error_event_table_ptr); |
7343 | sc->sc_uc.uc_log_event_table | | 7343 | sc->sc_uc.uc_log_event_table |
7344 | = le32toh(resp3->log_event_table_ptr); | | 7344 | = le32toh(resp3->log_event_table_ptr); |
7345 | sc->sched_base = le32toh(resp3->scd_base_ptr); | | 7345 | sc->sched_base = le32toh(resp3->scd_base_ptr); |
7346 | sc->sc_uc.uc_umac_error_event_table | | 7346 | sc->sc_uc.uc_umac_error_event_table |
7347 | = le32toh(resp3->error_info_addr); | | 7347 | = le32toh(resp3->error_info_addr); |
7348 | if (resp3->status == IWM_ALIVE_STATUS_OK) | | 7348 | if (resp3->status == IWM_ALIVE_STATUS_OK) |
7349 | sc->sc_uc.uc_ok = 1; | | 7349 | sc->sc_uc.uc_ok = 1; |
7350 | else | | 7350 | else |
7351 | sc->sc_uc.uc_ok = 0; | | 7351 | sc->sc_uc.uc_ok = 0; |
7352 | } | | 7352 | } |
7353 | | | 7353 | |
7354 | sc->sc_uc.uc_intr = 1; | | 7354 | sc->sc_uc.uc_intr = 1; |
7355 | wakeup(&sc->sc_uc); | | 7355 | wakeup(&sc->sc_uc); |
7356 | break; | | 7356 | break; |
7357 | } | | 7357 | } |
7358 | | | 7358 | |
7359 | case IWM_CALIB_RES_NOTIF_PHY_DB: { | | 7359 | case IWM_CALIB_RES_NOTIF_PHY_DB: { |
7360 | struct iwm_calib_res_notif_phy_db *phy_db_notif; | | 7360 | struct iwm_calib_res_notif_phy_db *phy_db_notif; |
7361 | SYNC_RESP_STRUCT(phy_db_notif, pkt); | | 7361 | SYNC_RESP_STRUCT(phy_db_notif, pkt); |
7362 | uint16_t size = le16toh(phy_db_notif->length); | | 7362 | uint16_t size = le16toh(phy_db_notif->length); |
7363 | bus_dmamap_sync(sc->sc_dmat, data->map, | | 7363 | bus_dmamap_sync(sc->sc_dmat, data->map, |
7364 | sizeof(*pkt) + sizeof(*phy_db_notif), | | 7364 | sizeof(*pkt) + sizeof(*phy_db_notif), |
7365 | size, BUS_DMASYNC_POSTREAD); | | 7365 | size, BUS_DMASYNC_POSTREAD); |
7366 | iwm_phy_db_set_section(sc, phy_db_notif, size); | | 7366 | iwm_phy_db_set_section(sc, phy_db_notif, size); |
7367 | break; | | 7367 | break; |
7368 | } | | 7368 | } |
7369 | | | 7369 | |
7370 | case IWM_STATISTICS_NOTIFICATION: { | | 7370 | case IWM_STATISTICS_NOTIFICATION: { |
7371 | struct iwm_notif_statistics *stats; | | 7371 | struct iwm_notif_statistics *stats; |
7372 | SYNC_RESP_STRUCT(stats, pkt); | | 7372 | SYNC_RESP_STRUCT(stats, pkt); |
7373 | memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats)); | | 7373 | memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats)); |
7374 | sc->sc_noise = iwm_get_noise(&stats->rx.general); | | 7374 | sc->sc_noise = iwm_get_noise(&stats->rx.general); |
7375 | break; | | 7375 | break; |
7376 | } | | 7376 | } |
7377 | | | 7377 | |
7378 | case IWM_NVM_ACCESS_CMD: | | 7378 | case IWM_NVM_ACCESS_CMD: |
7379 | case IWM_MCC_UPDATE_CMD: | | 7379 | case IWM_MCC_UPDATE_CMD: |
7380 | if (sc->sc_wantresp == ((qid << 16) | idx)) { | | 7380 | if (sc->sc_wantresp == ((qid << 16) | idx)) { |
7381 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, | | 7381 | bus_dmamap_sync(sc->sc_dmat, data->map, 0, |
7382 | sizeof(sc->sc_cmd_resp), | | 7382 | sizeof(sc->sc_cmd_resp), |
7383 | BUS_DMASYNC_POSTREAD); | | 7383 | BUS_DMASYNC_POSTREAD); |
7384 | memcpy(sc->sc_cmd_resp, | | 7384 | memcpy(sc->sc_cmd_resp, |
7385 | pkt, sizeof(sc->sc_cmd_resp)); | | 7385 | pkt, sizeof(sc->sc_cmd_resp)); |
7386 | } | | 7386 | } |
7387 | break; | | 7387 | break; |
7388 | | | 7388 | |
7389 | case IWM_MCC_CHUB_UPDATE_CMD: { | | 7389 | case IWM_MCC_CHUB_UPDATE_CMD: { |
7390 | struct iwm_mcc_chub_notif *notif; | | 7390 | struct iwm_mcc_chub_notif *notif; |
7391 | SYNC_RESP_STRUCT(notif, pkt); | | 7391 | SYNC_RESP_STRUCT(notif, pkt); |
7392 | | | 7392 | |
7393 | sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8; | | 7393 | sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8; |
7394 | sc->sc_fw_mcc[1] = notif->mcc & 0xff; | | 7394 | sc->sc_fw_mcc[1] = notif->mcc & 0xff; |
7395 | sc->sc_fw_mcc[2] = '\0'; | | 7395 | sc->sc_fw_mcc[2] = '\0'; |
7396 | break; | | 7396 | break; |
7397 | } | | 7397 | } |
7398 | | | 7398 | |
7399 | case IWM_DTS_MEASUREMENT_NOTIFICATION: | | 7399 | case IWM_DTS_MEASUREMENT_NOTIFICATION: |
7400 | case IWM_WIDE_ID(IWM_PHY_OPS_GROUP, | | 7400 | case IWM_WIDE_ID(IWM_PHY_OPS_GROUP, |
7401 | IWM_DTS_MEASUREMENT_NOTIF_WIDE): { | | 7401 | IWM_DTS_MEASUREMENT_NOTIF_WIDE): { |
7402 | struct iwm_dts_measurement_notif_v1 *notif1; | | 7402 | struct iwm_dts_measurement_notif_v1 *notif1; |
7403 | struct iwm_dts_measurement_notif_v2 *notif2; | | 7403 | struct iwm_dts_measurement_notif_v2 *notif2; |
7404 | | | 7404 | |
7405 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*notif1)) { | | 7405 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*notif1)) { |
7406 | SYNC_RESP_STRUCT(notif1, pkt); | | 7406 | SYNC_RESP_STRUCT(notif1, pkt); |
7407 | DPRINTF(("%s: DTS temp=%d \n", | | 7407 | DPRINTF(("%s: DTS temp=%d \n", |
7408 | DEVNAME(sc), notif1->temp)); | | 7408 | DEVNAME(sc), notif1->temp)); |
7409 | break; | | 7409 | break; |
7410 | } | | 7410 | } |
7411 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*notif2)) { | | 7411 | if (iwm_rx_packet_payload_len(pkt) == sizeof(*notif2)) { |
7412 | SYNC_RESP_STRUCT(notif2, pkt); | | 7412 | SYNC_RESP_STRUCT(notif2, pkt); |
7413 | DPRINTF(("%s: DTS temp=%d \n", | | 7413 | DPRINTF(("%s: DTS temp=%d \n", |
7414 | DEVNAME(sc), notif2->temp)); | | 7414 | DEVNAME(sc), notif2->temp)); |
7415 | break; | | 7415 | break; |
7416 | } | | 7416 | } |
7417 | break; | | 7417 | break; |
7418 | } | | 7418 | } |
7419 | | | 7419 | |
7420 | case IWM_PHY_CONFIGURATION_CMD: | | 7420 | case IWM_PHY_CONFIGURATION_CMD: |
7421 | case IWM_TX_ANT_CONFIGURATION_CMD: | | 7421 | case IWM_TX_ANT_CONFIGURATION_CMD: |
7422 | case IWM_ADD_STA: | | 7422 | case IWM_ADD_STA: |
7423 | case IWM_MAC_CONTEXT_CMD: | | 7423 | case IWM_MAC_CONTEXT_CMD: |
7424 | case IWM_REPLY_SF_CFG_CMD: | | 7424 | case IWM_REPLY_SF_CFG_CMD: |
7425 | case IWM_POWER_TABLE_CMD: | | 7425 | case IWM_POWER_TABLE_CMD: |
7426 | case IWM_PHY_CONTEXT_CMD: | | 7426 | case IWM_PHY_CONTEXT_CMD: |
7427 | case IWM_BINDING_CONTEXT_CMD: | | 7427 | case IWM_BINDING_CONTEXT_CMD: |
7428 | case IWM_TIME_EVENT_CMD: | | 7428 | case IWM_TIME_EVENT_CMD: |
7429 | case IWM_SCAN_REQUEST_CMD: | | 7429 | case IWM_SCAN_REQUEST_CMD: |
7430 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD): | | 7430 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD): |
7431 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC): | | 7431 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC): |
7432 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_ABORT_UMAC): | | 7432 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_ABORT_UMAC): |
7433 | case IWM_SCAN_OFFLOAD_REQUEST_CMD: | | 7433 | case IWM_SCAN_OFFLOAD_REQUEST_CMD: |
7434 | case IWM_SCAN_OFFLOAD_ABORT_CMD: | | 7434 | case IWM_SCAN_OFFLOAD_ABORT_CMD: |
7435 | case IWM_REPLY_BEACON_FILTERING_CMD: | | 7435 | case IWM_REPLY_BEACON_FILTERING_CMD: |
7436 | case IWM_MAC_PM_POWER_TABLE: | | 7436 | case IWM_MAC_PM_POWER_TABLE: |
7437 | case IWM_TIME_QUOTA_CMD: | | 7437 | case IWM_TIME_QUOTA_CMD: |
7438 | case IWM_REMOVE_STA: | | 7438 | case IWM_REMOVE_STA: |
7439 | case IWM_TXPATH_FLUSH: | | 7439 | case IWM_TXPATH_FLUSH: |
7440 | case IWM_LQ_CMD: | | 7440 | case IWM_LQ_CMD: |
7441 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_FW_PAGING_BLOCK_CMD): | | 7441 | case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_FW_PAGING_BLOCK_CMD): |
7442 | case IWM_BT_CONFIG: | | 7442 | case IWM_BT_CONFIG: |
7443 | case IWM_REPLY_THERMAL_MNG_BACKOFF: | | 7443 | case IWM_REPLY_THERMAL_MNG_BACKOFF: |
7444 | SYNC_RESP_STRUCT(cresp, pkt); | | 7444 | SYNC_RESP_STRUCT(cresp, pkt); |
7445 | if (sc->sc_wantresp == ((qid << 16) | idx)) { | | 7445 | if (sc->sc_wantresp == ((qid << 16) | idx)) { |
7446 | memcpy(sc->sc_cmd_resp, | | 7446 | memcpy(sc->sc_cmd_resp, |
7447 | pkt, sizeof(*pkt) + sizeof(*cresp)); | | 7447 | pkt, sizeof(*pkt) + sizeof(*cresp)); |
7448 | } | | 7448 | } |
7449 | break; | | 7449 | break; |
7450 | | | 7450 | |
7451 | /* ignore */ | | 7451 | /* ignore */ |
7452 | case IWM_PHY_DB_CMD: | | 7452 | case IWM_PHY_DB_CMD: |
7453 | break; | | 7453 | break; |
7454 | | | 7454 | |
7455 | case IWM_INIT_COMPLETE_NOTIF: | | 7455 | case IWM_INIT_COMPLETE_NOTIF: |
7456 | sc->sc_init_complete = 1; | | 7456 | sc->sc_init_complete = 1; |
7457 | wakeup(&sc->sc_init_complete); | | 7457 | wakeup(&sc->sc_init_complete); |
7458 | break; | | 7458 | break; |
7459 | | | 7459 | |
7460 | case IWM_SCAN_OFFLOAD_COMPLETE: { | | 7460 | case IWM_SCAN_OFFLOAD_COMPLETE: { |
7461 | struct iwm_periodic_scan_complete *notif; | | 7461 | struct iwm_periodic_scan_complete *notif; |
7462 | SYNC_RESP_STRUCT(notif, pkt); | | 7462 | SYNC_RESP_STRUCT(notif, pkt); |
7463 | break; | | 7463 | break; |
7464 | } | | 7464 | } |
7465 | | | 7465 | |
7466 | case IWM_SCAN_ITERATION_COMPLETE: { | | 7466 | case IWM_SCAN_ITERATION_COMPLETE: { |
7467 | struct iwm_lmac_scan_complete_notif *notif; | | 7467 | struct iwm_lmac_scan_complete_notif *notif; |
7468 | SYNC_RESP_STRUCT(notif, pkt); | | 7468 | SYNC_RESP_STRUCT(notif, pkt); |
7469 | if (ISSET(sc->sc_flags, IWM_FLAG_SCANNING)) { | | 7469 | if (ISSET(sc->sc_flags, IWM_FLAG_SCANNING)) { |
7470 | CLR(sc->sc_flags, IWM_FLAG_SCANNING); | | 7470 | CLR(sc->sc_flags, IWM_FLAG_SCANNING); |
7471 | iwm_endscan(sc); | | 7471 | iwm_endscan(sc); |
7472 | } | | 7472 | } |
7473 | break; | | 7473 | break; |
7474 | } | | 7474 | } |
7475 | | | 7475 | |
7476 | case IWM_SCAN_COMPLETE_UMAC: { | | 7476 | case IWM_SCAN_COMPLETE_UMAC: { |
7477 | struct iwm_umac_scan_complete *notif; | | 7477 | struct iwm_umac_scan_complete *notif; |
7478 | SYNC_RESP_STRUCT(notif, pkt); | | 7478 | SYNC_RESP_STRUCT(notif, pkt); |
7479 | if (ISSET(sc->sc_flags, IWM_FLAG_SCANNING)) { | | 7479 | if (ISSET(sc->sc_flags, IWM_FLAG_SCANNING)) { |
7480 | CLR(sc->sc_flags, IWM_FLAG_SCANNING); | | 7480 | CLR(sc->sc_flags, IWM_FLAG_SCANNING); |
7481 | iwm_endscan(sc); | | 7481 | iwm_endscan(sc); |
7482 | } | | 7482 | } |
7483 | break; | | 7483 | break; |
7484 | } | | 7484 | } |
7485 | | | 7485 | |
7486 | case IWM_SCAN_ITERATION_COMPLETE_UMAC: { | | 7486 | case IWM_SCAN_ITERATION_COMPLETE_UMAC: { |
7487 | struct iwm_umac_scan_iter_complete_notif *notif; | | 7487 | struct iwm_umac_scan_iter_complete_notif *notif; |
7488 | SYNC_RESP_STRUCT(notif, pkt); | | 7488 | SYNC_RESP_STRUCT(notif, pkt); |
7489 | if (ISSET(sc->sc_flags, IWM_FLAG_SCANNING)) { | | 7489 | if (ISSET(sc->sc_flags, IWM_FLAG_SCANNING)) { |
7490 | CLR(sc->sc_flags, IWM_FLAG_SCANNING); | | 7490 | CLR(sc->sc_flags, IWM_FLAG_SCANNING); |
7491 | iwm_endscan(sc); | | 7491 | iwm_endscan(sc); |
7492 | } | | 7492 | } |
7493 | break; | | 7493 | break; |
7494 | } | | 7494 | } |
7495 | | | 7495 | |
7496 | case IWM_REPLY_ERROR: { | | 7496 | case IWM_REPLY_ERROR: { |
7497 | struct iwm_error_resp *resp; | | 7497 | struct iwm_error_resp *resp; |
7498 | SYNC_RESP_STRUCT(resp, pkt); | | 7498 | SYNC_RESP_STRUCT(resp, pkt); |
7499 | aprint_error_dev(sc->sc_dev, | | 7499 | aprint_error_dev(sc->sc_dev, |
7500 | "firmware error 0x%x, cmd 0x%x\n", | | 7500 | "firmware error 0x%x, cmd 0x%x\n", |
7501 | le32toh(resp->error_type), resp->cmd_id); | | 7501 | le32toh(resp->error_type), resp->cmd_id); |
7502 | break; | | 7502 | break; |
7503 | } | | 7503 | } |
7504 | | | 7504 | |
7505 | case IWM_TIME_EVENT_NOTIFICATION: { | | 7505 | case IWM_TIME_EVENT_NOTIFICATION: { |
7506 | struct iwm_time_event_notif *notif; | | 7506 | struct iwm_time_event_notif *notif; |
7507 | SYNC_RESP_STRUCT(notif, pkt); | | 7507 | SYNC_RESP_STRUCT(notif, pkt); |
7508 | break; | | 7508 | break; |
7509 | } | | 7509 | } |
7510 | | | 7510 | |
7511 | case IWM_DEBUG_LOG_MSG: | | 7511 | case IWM_DEBUG_LOG_MSG: |
7512 | break; | | 7512 | break; |
7513 | | | 7513 | |
7514 | case IWM_MCAST_FILTER_CMD: | | 7514 | case IWM_MCAST_FILTER_CMD: |
7515 | break; | | 7515 | break; |
7516 | | | 7516 | |
7517 | case IWM_SCD_QUEUE_CFG: { | | 7517 | case IWM_SCD_QUEUE_CFG: { |
7518 | struct iwm_scd_txq_cfg_rsp *rsp; | | 7518 | struct iwm_scd_txq_cfg_rsp *rsp; |
7519 | SYNC_RESP_STRUCT(rsp, pkt); | | 7519 | SYNC_RESP_STRUCT(rsp, pkt); |
7520 | break; | | 7520 | break; |
7521 | } | | 7521 | } |
7522 | | | 7522 | |
7523 | default: | | 7523 | default: |
7524 | aprint_error_dev(sc->sc_dev, | | 7524 | aprint_error_dev(sc->sc_dev, |
7525 | "unhandled firmware response 0x%x 0x%x/0x%x " | | 7525 | "unhandled firmware response 0x%x 0x%x/0x%x " |
7526 | "rx ring %d[%d]\n", | | 7526 | "rx ring %d[%d]\n", |
7527 | code, pkt->hdr.code, pkt->len_n_flags, qid, idx); | | 7527 | code, pkt->hdr.code, pkt->len_n_flags, qid, idx); |
7528 | break; | | 7528 | break; |
7529 | } | | 7529 | } |
7530 | | | 7530 | |
7531 | /* | | 7531 | /* |
7532 | * uCode sets bit 0x80 when it originates the notification, | | 7532 | * uCode sets bit 0x80 when it originates the notification, |
7533 | * i.e. when the notification is not a direct response to a | | 7533 | * i.e. when the notification is not a direct response to a |
7534 | * command sent by the driver. | | 7534 | * command sent by the driver. |
7535 | * For example, uCode issues IWM_REPLY_RX when it sends a | | 7535 | * For example, uCode issues IWM_REPLY_RX when it sends a |
7536 | * received frame to the driver. | | 7536 | * received frame to the driver. |
7537 | */ | | 7537 | */ |
7538 | if (!(orig_qid & (1 << 7))) { | | 7538 | if (!(orig_qid & (1 << 7))) { |
7539 | iwm_cmd_done(sc, qid, idx); | | 7539 | iwm_cmd_done(sc, qid, idx); |
7540 | } | | 7540 | } |
7541 | | | 7541 | |
7542 | ADVANCE_RXQ(sc); | | 7542 | ADVANCE_RXQ(sc); |
7543 | } | | 7543 | } |
7544 | | | 7544 | |
7545 | /* | | 7545 | /* |
7546 | * Seems like the hardware gets upset unless we align the write by 8?? | | 7546 | * Seems like the hardware gets upset unless we align the write by 8?? |
7547 | */ | | 7547 | */ |
7548 | hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1; | | 7548 | hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1; |
7549 | IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7); | | 7549 | IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7); |
7550 | } | | 7550 | } |
7551 | | | 7551 | |
7552 | static int | | 7552 | static int |
7553 | iwm_intr(void *arg) | | 7553 | iwm_intr(void *arg) |
7554 | { | | 7554 | { |
7555 | struct iwm_softc *sc = arg; | | 7555 | struct iwm_softc *sc = arg; |
7556 | | | 7556 | |
7557 | /* Disable interrupts */ | | 7557 | /* Disable interrupts */ |
7558 | IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); | | 7558 | IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); |
7559 | | | 7559 | |
7560 | softint_schedule(sc->sc_soft_ih); | | 7560 | softint_schedule(sc->sc_soft_ih); |
7561 | return 1; | | 7561 | return 1; |
7562 | } | | 7562 | } |
7563 | | | 7563 | |
7564 | static void | | 7564 | static void |
7565 | iwm_softintr(void *arg) | | 7565 | iwm_softintr(void *arg) |
7566 | { | | 7566 | { |
7567 | struct iwm_softc *sc = arg; | | 7567 | struct iwm_softc *sc = arg; |
7568 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); | | 7568 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); |
7569 | uint32_t r1, r2; | | 7569 | uint32_t r1, r2; |
7570 | int isperiodic = 0, s; | | 7570 | int isperiodic = 0, s; |
7571 | | | 7571 | |
7572 | if (__predict_true(sc->sc_flags & IWM_FLAG_USE_ICT)) { | | 7572 | if (__predict_true(sc->sc_flags & IWM_FLAG_USE_ICT)) { |
7573 | uint32_t *ict = sc->ict_dma.vaddr; | | 7573 | uint32_t *ict = sc->ict_dma.vaddr; |
7574 | int tmp; | | 7574 | int tmp; |
7575 | | | 7575 | |
7576 | bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, | | 7576 | bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, |
7577 | 0, sc->ict_dma.size, BUS_DMASYNC_POSTREAD); | | 7577 | 0, sc->ict_dma.size, BUS_DMASYNC_POSTREAD); |
7578 | tmp = htole32(ict[sc->ict_cur]); | | 7578 | tmp = htole32(ict[sc->ict_cur]); |
7579 | if (tmp == 0) | | 7579 | if (tmp == 0) |
7580 | goto out_ena; /* Interrupt not for us. */ | | 7580 | goto out_ena; /* Interrupt not for us. */ |
7581 | | | 7581 | |
7582 | /* | | 7582 | /* |
7583 | * ok, there was something. keep plowing until we have all. | | 7583 | * ok, there was something. keep plowing until we have all. |
7584 | */ | | 7584 | */ |
7585 | r1 = r2 = 0; | | 7585 | r1 = r2 = 0; |
7586 | while (tmp) { | | 7586 | while (tmp) { |
7587 | r1 |= tmp; | | 7587 | r1 |= tmp; |
7588 | ict[sc->ict_cur] = 0; /* Acknowledge. */ | | 7588 | ict[sc->ict_cur] = 0; /* Acknowledge. */ |
7589 | sc->ict_cur = (sc->ict_cur + 1) % IWM_ICT_COUNT; | | 7589 | sc->ict_cur = (sc->ict_cur + 1) % IWM_ICT_COUNT; |
7590 | tmp = htole32(ict[sc->ict_cur]); | | 7590 | tmp = htole32(ict[sc->ict_cur]); |
7591 | } | | 7591 | } |
7592 | | | 7592 | |
7593 | bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, | | 7593 | bus_dmamap_sync(sc->sc_dmat, sc->ict_dma.map, |
7594 | 0, sc->ict_dma.size, BUS_DMASYNC_PREWRITE); | | 7594 | 0, sc->ict_dma.size, BUS_DMASYNC_PREWRITE); |
7595 | | | 7595 | |
7596 | /* this is where the fun begins. don't ask */ | | 7596 | /* this is where the fun begins. don't ask */ |
7597 | if (r1 == 0xffffffff) | | 7597 | if (r1 == 0xffffffff) |
7598 | r1 = 0; | | 7598 | r1 = 0; |
7599 | | | 7599 | |
7600 | /* i am not expected to understand this */ | | 7600 | /* i am not expected to understand this */ |
7601 | if (r1 & 0xc0000) | | 7601 | if (r1 & 0xc0000) |
7602 | r1 |= 0x8000; | | 7602 | r1 |= 0x8000; |
7603 | r1 = (0xff & r1) | ((0xff00 & r1) << 16); | | 7603 | r1 = (0xff & r1) | ((0xff00 & r1) << 16); |
7604 | } else { | | 7604 | } else { |
7605 | r1 = IWM_READ(sc, IWM_CSR_INT); | | 7605 | r1 = IWM_READ(sc, IWM_CSR_INT); |
7606 | if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) | | 7606 | if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) |
7607 | return; /* Hardware gone! */ | | 7607 | return; /* Hardware gone! */ |
7608 | r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); | | 7608 | r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); |
7609 | } | | 7609 | } |
7610 | if (r1 == 0 && r2 == 0) { | | 7610 | if (r1 == 0 && r2 == 0) { |
7611 | goto out_ena; /* Interrupt not for us. */ | | 7611 | goto out_ena; /* Interrupt not for us. */ |
7612 | } | | 7612 | } |
7613 | | | 7613 | |
7614 | /* Acknowledge interrupts. */ | | 7614 | /* Acknowledge interrupts. */ |
7615 | IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); | | 7615 | IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); |
7616 | if (__predict_false(!(sc->sc_flags & IWM_FLAG_USE_ICT))) | | 7616 | if (__predict_false(!(sc->sc_flags & IWM_FLAG_USE_ICT))) |
7617 | IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, r2); | | 7617 | IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, r2); |
7618 | | | 7618 | |
7619 | if (r1 & IWM_CSR_INT_BIT_SW_ERR) { | | 7619 | if (r1 & IWM_CSR_INT_BIT_SW_ERR) { |
7620 | #ifdef IWM_DEBUG | | 7620 | #ifdef IWM_DEBUG |
7621 | int i; | | 7621 | int i; |
7622 | | | 7622 | |
7623 | iwm_nic_error(sc); | | 7623 | iwm_nic_error(sc); |
7624 | | | 7624 | |
7625 | /* Dump driver status (TX and RX rings) while we're here. */ | | 7625 | /* Dump driver status (TX and RX rings) while we're here. */ |
7626 | DPRINTF(("driver status:\n")); | | 7626 | DPRINTF(("driver status:\n")); |
7627 | for (i = 0; i < IWM_MAX_QUEUES; i++) { | | 7627 | for (i = 0; i < IWM_MAX_QUEUES; i++) { |
7628 | struct iwm_tx_ring *ring = &sc->txq[i]; | | 7628 | struct iwm_tx_ring *ring = &sc->txq[i]; |
7629 | DPRINTF((" tx ring %2d: qid=%-2d cur=%-3d " | | 7629 | DPRINTF((" tx ring %2d: qid=%-2d cur=%-3d " |
7630 | "queued=%-3d\n", | | 7630 | "queued=%-3d\n", |
7631 | i, ring->qid, ring->cur, ring->queued)); | | 7631 | i, ring->qid, ring->cur, ring->queued)); |
7632 | } | | 7632 | } |
7633 | DPRINTF((" rx ring: cur=%d\n", sc->rxq.cur)); | | 7633 | DPRINTF((" rx ring: cur=%d\n", sc->rxq.cur)); |
7634 | DPRINTF((" 802.11 state %s\n", | | 7634 | DPRINTF((" 802.11 state %s\n", |
7635 | ieee80211_state_name[sc->sc_ic.ic_state])); | | 7635 | ieee80211_state_name[sc->sc_ic.ic_state])); |
7636 | #endif | | 7636 | #endif |
7637 | | | 7637 | |
7638 | aprint_error_dev(sc->sc_dev, "fatal firmware error\n"); | | 7638 | aprint_error_dev(sc->sc_dev, "fatal firmware error\n"); |
7639 | fatal: | | 7639 | fatal: |
7640 | s = splnet(); | | 7640 | s = splnet(); |
7641 | ifp->if_flags &= ~IFF_UP; | | 7641 | ifp->if_flags &= ~IFF_UP; |
7642 | iwm_stop(ifp, 1); | | 7642 | iwm_stop(ifp, 1); |
7643 | splx(s); | | 7643 | splx(s); |
7644 | /* Don't restore interrupt mask */ | | 7644 | /* Don't restore interrupt mask */ |
7645 | return; | | 7645 | return; |
7646 | | | 7646 | |
7647 | } | | 7647 | } |
7648 | | | 7648 | |
7649 | if (r1 & IWM_CSR_INT_BIT_HW_ERR) { | | 7649 | if (r1 & IWM_CSR_INT_BIT_HW_ERR) { |
7650 | aprint_error_dev(sc->sc_dev, | | 7650 | aprint_error_dev(sc->sc_dev, |
7651 | "hardware error, stopping device\n"); | | 7651 | "hardware error, stopping device\n"); |
7652 | goto fatal; | | 7652 | goto fatal; |
7653 | } | | 7653 | } |
7654 | | | 7654 | |
7655 | /* firmware chunk loaded */ | | 7655 | /* firmware chunk loaded */ |
7656 | if (r1 & IWM_CSR_INT_BIT_FH_TX) { | | 7656 | if (r1 & IWM_CSR_INT_BIT_FH_TX) { |
7657 | IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); | | 7657 | IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); |
7658 | sc->sc_fw_chunk_done = 1; | | 7658 | sc->sc_fw_chunk_done = 1; |
7659 | wakeup(&sc->sc_fw); | | 7659 | wakeup(&sc->sc_fw); |
7660 | } | | 7660 | } |
7661 | | | 7661 | |
7662 | if (r1 & IWM_CSR_INT_BIT_RF_KILL) { | | 7662 | if (r1 & IWM_CSR_INT_BIT_RF_KILL) { |
7663 | if (iwm_check_rfkill(sc) && (ifp->if_flags & IFF_UP)) | | 7663 | if (iwm_check_rfkill(sc) && (ifp->if_flags & IFF_UP)) |
7664 | goto fatal; | | 7664 | goto fatal; |
7665 | } | | 7665 | } |
7666 | | | 7666 | |
7667 | if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) { | | 7667 | if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) { |
7668 | IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC); | | 7668 | IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC); |
7669 | if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0) | | 7669 | if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0) |
7670 | IWM_WRITE_1(sc, | | 7670 | IWM_WRITE_1(sc, |
7671 | IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS); | | 7671 | IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS); |
7672 | isperiodic = 1; | | 7672 | isperiodic = 1; |
7673 | } | | 7673 | } |
7674 | | | 7674 | |
7675 | if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || | | 7675 | if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || |
7676 | isperiodic) { | | 7676 | isperiodic) { |
7677 | IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK); | | 7677 | IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK); |
7678 | | | 7678 | |
7679 | iwm_notif_intr(sc); | | 7679 | iwm_notif_intr(sc); |
7680 | | | 7680 | |
7681 | /* enable periodic interrupt, see above */ | | 7681 | /* enable periodic interrupt, see above */ |
7682 | if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && | | 7682 | if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && |
7683 | !isperiodic) | | 7683 | !isperiodic) |
7684 | IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, | | 7684 | IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, |
7685 | IWM_CSR_INT_PERIODIC_ENA); | | 7685 | IWM_CSR_INT_PERIODIC_ENA); |
7686 | } | | 7686 | } |
7687 | | | 7687 | |
7688 | out_ena: | | 7688 | out_ena: |
7689 | iwm_restore_interrupts(sc); | | 7689 | iwm_restore_interrupts(sc); |
7690 | } | | 7690 | } |
7691 | | | 7691 | |
7692 | /* | | 7692 | /* |
7693 | * Autoconf glue-sniffing | | 7693 | * Autoconf glue-sniffing |
7694 | */ | | 7694 | */ |
7695 | | | 7695 | |
7696 | static const pci_product_id_t iwm_devices[] = { | | 7696 | static const pci_product_id_t iwm_devices[] = { |
7697 | PCI_PRODUCT_INTEL_WIFI_LINK_7260_1, | | 7697 | PCI_PRODUCT_INTEL_WIFI_LINK_7260_1, |
7698 | PCI_PRODUCT_INTEL_WIFI_LINK_7260_2, | | 7698 | PCI_PRODUCT_INTEL_WIFI_LINK_7260_2, |
7699 | PCI_PRODUCT_INTEL_WIFI_LINK_3160_1, | | 7699 | PCI_PRODUCT_INTEL_WIFI_LINK_3160_1, |
7700 | PCI_PRODUCT_INTEL_WIFI_LINK_3160_2, | | 7700 | PCI_PRODUCT_INTEL_WIFI_LINK_3160_2, |
7701 | PCI_PRODUCT_INTEL_WIFI_LINK_7265_1, | | 7701 | PCI_PRODUCT_INTEL_WIFI_LINK_7265_1, |
7702 | PCI_PRODUCT_INTEL_WIFI_LINK_7265_2, | | 7702 | PCI_PRODUCT_INTEL_WIFI_LINK_7265_2, |
7703 | PCI_PRODUCT_INTEL_WIFI_LINK_3165_1, | | 7703 | PCI_PRODUCT_INTEL_WIFI_LINK_3165_1, |
7704 | PCI_PRODUCT_INTEL_WIFI_LINK_3165_2, | | 7704 | PCI_PRODUCT_INTEL_WIFI_LINK_3165_2, |
7705 | PCI_PRODUCT_INTEL_WIFI_LINK_3168, | | 7705 | PCI_PRODUCT_INTEL_WIFI_LINK_3168, |
7706 | PCI_PRODUCT_INTEL_WIFI_LINK_8260_1, | | 7706 | PCI_PRODUCT_INTEL_WIFI_LINK_8260_1, |
7707 | PCI_PRODUCT_INTEL_WIFI_LINK_8260_2, | | 7707 | PCI_PRODUCT_INTEL_WIFI_LINK_8260_2, |
7708 | PCI_PRODUCT_INTEL_WIFI_LINK_4165_1, | | 7708 | PCI_PRODUCT_INTEL_WIFI_LINK_4165_1, |
7709 | PCI_PRODUCT_INTEL_WIFI_LINK_4165_2, | | 7709 | PCI_PRODUCT_INTEL_WIFI_LINK_4165_2, |
7710 | PCI_PRODUCT_INTEL_WIFI_LINK_8265, | | 7710 | PCI_PRODUCT_INTEL_WIFI_LINK_8265, |
7711 | }; | | 7711 | }; |
7712 | | | 7712 | |
7713 | static int | | 7713 | static int |
7714 | iwm_match(device_t parent, cfdata_t match __unused, void *aux) | | 7714 | iwm_match(device_t parent, cfdata_t match __unused, void *aux) |
7715 | { | | 7715 | { |
7716 | struct pci_attach_args *pa = aux; | | 7716 | struct pci_attach_args *pa = aux; |
7717 | | | 7717 | |
7718 | if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) | | 7718 | if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) |
7719 | return 0; | | 7719 | return 0; |
7720 | | | 7720 | |
7721 | for (size_t i = 0; i < __arraycount(iwm_devices); i++) | | 7721 | for (size_t i = 0; i < __arraycount(iwm_devices); i++) |
7722 | if (PCI_PRODUCT(pa->pa_id) == iwm_devices[i]) | | 7722 | if (PCI_PRODUCT(pa->pa_id) == iwm_devices[i]) |
7723 | return 1; | | 7723 | return 1; |
7724 | | | 7724 | |
7725 | return 0; | | 7725 | return 0; |
7726 | } | | 7726 | } |
7727 | | | 7727 | |
7728 | static int | | 7728 | static int |
7729 | iwm_preinit(struct iwm_softc *sc) | | 7729 | iwm_preinit(struct iwm_softc *sc) |
7730 | { | | 7730 | { |
7731 | struct ieee80211com *ic = &sc->sc_ic; | | 7731 | struct ieee80211com *ic = &sc->sc_ic; |
| | | 7732 | struct ifnet *ifp = IC2IFP(&sc->sc_ic);; |
7732 | int err; | | 7733 | int err; |
7733 | | | 7734 | |
7734 | if (ISSET(sc->sc_flags, IWM_FLAG_ATTACHED)) | | 7735 | if (ISSET(sc->sc_flags, IWM_FLAG_ATTACHED)) |
7735 | return 0; | | 7736 | return 0; |
7736 | | | 7737 | |
7737 | err = iwm_start_hw(sc); | | 7738 | err = iwm_start_hw(sc); |
7738 | if (err) { | | 7739 | if (err) { |
7739 | aprint_error_dev(sc->sc_dev, "could not initialize hardware\n"); | | 7740 | aprint_error_dev(sc->sc_dev, "could not initialize hardware\n"); |
7740 | return err; | | 7741 | return err; |
7741 | } | | 7742 | } |
7742 | | | 7743 | |
7743 | err = iwm_run_init_mvm_ucode(sc, 1); | | 7744 | err = iwm_run_init_mvm_ucode(sc, 1); |
7744 | iwm_stop_device(sc); | | 7745 | iwm_stop_device(sc); |
7745 | if (err) | | 7746 | if (err) |
7746 | return err; | | 7747 | return err; |
7747 | | | 7748 | |
7748 | sc->sc_flags |= IWM_FLAG_ATTACHED; | | 7749 | sc->sc_flags |= IWM_FLAG_ATTACHED; |
7749 | | | 7750 | |
7750 | aprint_normal_dev(sc->sc_dev, "hw rev 0x%x, fw ver %s, address %s\n", | | 7751 | aprint_normal_dev(sc->sc_dev, "hw rev 0x%x, fw ver %s, address %s\n", |
7751 | sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, sc->sc_fwver, | | 7752 | sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, sc->sc_fwver, |
7752 | ether_sprintf(sc->sc_nvm.hw_addr)); | | 7753 | ether_sprintf(sc->sc_nvm.hw_addr)); |
7753 | | | 7754 | |
7754 | #ifndef IEEE80211_NO_HT | | 7755 | #ifndef IEEE80211_NO_HT |
7755 | if (sc->sc_nvm.sku_cap_11n_enable) | | 7756 | if (sc->sc_nvm.sku_cap_11n_enable) |
7756 | iwm_setup_ht_rates(sc); | | 7757 | iwm_setup_ht_rates(sc); |
7757 | #endif | | 7758 | #endif |
7758 | | | 7759 | |
7759 | /* not all hardware can do 5GHz band */ | | 7760 | /* not all hardware can do 5GHz band */ |
7760 | if (sc->sc_nvm.sku_cap_band_52GHz_enable) | | 7761 | if (sc->sc_nvm.sku_cap_band_52GHz_enable) |
7761 | ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a; | | 7762 | ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a; |
7762 | | | 7763 | |
| | | 7764 | ether_ifdetach(ifp); |
7763 | ieee80211_ifattach(ic); | | 7765 | ieee80211_ifattach(ic); |
7764 | | | 7766 | |
7765 | ic->ic_node_alloc = iwm_node_alloc; | | 7767 | ic->ic_node_alloc = iwm_node_alloc; |
7766 | | | 7768 | |
7767 | /* Override 802.11 state transition machine. */ | | 7769 | /* Override 802.11 state transition machine. */ |
7768 | sc->sc_newstate = ic->ic_newstate; | | 7770 | sc->sc_newstate = ic->ic_newstate; |
7769 | ic->ic_newstate = iwm_newstate; | | 7771 | ic->ic_newstate = iwm_newstate; |
7770 | ieee80211_media_init(ic, iwm_media_change, ieee80211_media_status); | | 7772 | ieee80211_media_init(ic, iwm_media_change, ieee80211_media_status); |
7771 | ieee80211_announce(ic); | | 7773 | ieee80211_announce(ic); |
7772 | | | 7774 | |
7773 | iwm_radiotap_attach(sc); | | 7775 | iwm_radiotap_attach(sc); |
7774 | | | 7776 | |
7775 | return 0; | | 7777 | return 0; |
7776 | } | | 7778 | } |
7777 | | | 7779 | |
7778 | static void | | 7780 | static void |
7779 | iwm_attach_hook(device_t dev) | | 7781 | iwm_attach_hook(device_t dev) |
7780 | { | | 7782 | { |
7781 | struct iwm_softc *sc = device_private(dev); | | 7783 | struct iwm_softc *sc = device_private(dev); |
7782 | | | 7784 | |
7783 | iwm_preinit(sc); | | 7785 | iwm_preinit(sc); |
7784 | } | | 7786 | } |
7785 | | | 7787 | |
7786 | static void | | 7788 | static void |
7787 | iwm_attach(device_t parent, device_t self, void *aux) | | 7789 | iwm_attach(device_t parent, device_t self, void *aux) |
7788 | { | | 7790 | { |
7789 | struct iwm_softc *sc = device_private(self); | | 7791 | struct iwm_softc *sc = device_private(self); |
7790 | struct pci_attach_args *pa = aux; | | 7792 | struct pci_attach_args *pa = aux; |
7791 | struct ieee80211com *ic = &sc->sc_ic; | | 7793 | struct ieee80211com *ic = &sc->sc_ic; |
7792 | struct ifnet *ifp = &sc->sc_ec.ec_if; | | 7794 | struct ifnet *ifp = &sc->sc_ec.ec_if; |
7793 | pcireg_t reg, memtype; | | 7795 | pcireg_t reg, memtype; |
7794 | char intrbuf[PCI_INTRSTR_LEN]; | | 7796 | char intrbuf[PCI_INTRSTR_LEN]; |
7795 | const char *intrstr; | | 7797 | const char *intrstr; |
7796 | int err; | | 7798 | int err; |
7797 | int txq_i; | | 7799 | int txq_i; |
7798 | const struct sysctlnode *node; | | 7800 | const struct sysctlnode *node; |
7799 | | | 7801 | |
7800 | sc->sc_dev = self; | | 7802 | sc->sc_dev = self; |
7801 | sc->sc_pct = pa->pa_pc; | | 7803 | sc->sc_pct = pa->pa_pc; |
7802 | sc->sc_pcitag = pa->pa_tag; | | 7804 | sc->sc_pcitag = pa->pa_tag; |
7803 | sc->sc_dmat = pa->pa_dmat; | | 7805 | sc->sc_dmat = pa->pa_dmat; |
7804 | sc->sc_pciid = pa->pa_id; | | 7806 | sc->sc_pciid = pa->pa_id; |
7805 | | | 7807 | |
7806 | pci_aprint_devinfo(pa, NULL); | | 7808 | pci_aprint_devinfo(pa, NULL); |
7807 | | | 7809 | |
7808 | if (workqueue_create(&sc->sc_nswq, "iwmns", | | 7810 | if (workqueue_create(&sc->sc_nswq, "iwmns", |
7809 | iwm_newstate_cb, sc, PRI_NONE, IPL_NET, 0)) | | 7811 | iwm_newstate_cb, sc, PRI_NONE, IPL_NET, 0)) |
7810 | panic("%s: could not create workqueue: newstate", | | 7812 | panic("%s: could not create workqueue: newstate", |
7811 | device_xname(self)); | | 7813 | device_xname(self)); |
7812 | sc->sc_soft_ih = softint_establish(SOFTINT_NET, iwm_softintr, sc); | | 7814 | sc->sc_soft_ih = softint_establish(SOFTINT_NET, iwm_softintr, sc); |
7813 | if (sc->sc_soft_ih == NULL) | | 7815 | if (sc->sc_soft_ih == NULL) |
7814 | panic("%s: could not establish softint", device_xname(self)); | | 7816 | panic("%s: could not establish softint", device_xname(self)); |
7815 | | | 7817 | |
7816 | /* | | 7818 | /* |
7817 | * Get the offset of the PCI Express Capability Structure in PCI | | 7819 | * Get the offset of the PCI Express Capability Structure in PCI |
7818 | * Configuration Space. | | 7820 | * Configuration Space. |
7819 | */ | | 7821 | */ |
7820 | err = pci_get_capability(sc->sc_pct, sc->sc_pcitag, | | 7822 | err = pci_get_capability(sc->sc_pct, sc->sc_pcitag, |
7821 | PCI_CAP_PCIEXPRESS, &sc->sc_cap_off, NULL); | | 7823 | PCI_CAP_PCIEXPRESS, &sc->sc_cap_off, NULL); |
7822 | if (err == 0) { | | 7824 | if (err == 0) { |
7823 | aprint_error_dev(self, | | 7825 | aprint_error_dev(self, |
7824 | "PCIe capability structure not found!\n"); | | 7826 | "PCIe capability structure not found!\n"); |
7825 | return; | | 7827 | return; |
7826 | } | | 7828 | } |
7827 | | | 7829 | |
7828 | /* Clear device-specific "PCI retry timeout" register (41h). */ | | 7830 | /* Clear device-specific "PCI retry timeout" register (41h). */ |
7829 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); | | 7831 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); |
7830 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg & ~0xff00); | | 7832 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg & ~0xff00); |
7831 | | | 7833 | |
7832 | /* Enable bus-mastering */ | | 7834 | /* Enable bus-mastering */ |
7833 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG); | | 7835 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG); |
7834 | reg |= PCI_COMMAND_MASTER_ENABLE; | | 7836 | reg |= PCI_COMMAND_MASTER_ENABLE; |
7835 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, reg); | | 7837 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, reg); |
7836 | | | 7838 | |
7837 | memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); | | 7839 | memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START); |
7838 | err = pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, | | 7840 | err = pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, |
7839 | &sc->sc_st, &sc->sc_sh, NULL, &sc->sc_sz); | | 7841 | &sc->sc_st, &sc->sc_sh, NULL, &sc->sc_sz); |
7840 | if (err) { | | 7842 | if (err) { |
7841 | aprint_error_dev(self, "can't map mem space\n"); | | 7843 | aprint_error_dev(self, "can't map mem space\n"); |
7842 | return; | | 7844 | return; |
7843 | } | | 7845 | } |
7844 | | | 7846 | |
7845 | /* Install interrupt handler. */ | | 7847 | /* Install interrupt handler. */ |
7846 | err = pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0); | | 7848 | err = pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0); |
7847 | if (err) { | | 7849 | if (err) { |
7848 | aprint_error_dev(self, "can't allocate interrupt\n"); | | 7850 | aprint_error_dev(self, "can't allocate interrupt\n"); |
7849 | return; | | 7851 | return; |
7850 | } | | 7852 | } |
7851 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG); | | 7853 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG); |
7852 | if (pci_intr_type(sc->sc_pct, sc->sc_pihp[0]) == PCI_INTR_TYPE_INTX) | | 7854 | if (pci_intr_type(sc->sc_pct, sc->sc_pihp[0]) == PCI_INTR_TYPE_INTX) |
7853 | CLR(reg, PCI_COMMAND_INTERRUPT_DISABLE); | | 7855 | CLR(reg, PCI_COMMAND_INTERRUPT_DISABLE); |
7854 | else | | 7856 | else |
7855 | SET(reg, PCI_COMMAND_INTERRUPT_DISABLE); | | 7857 | SET(reg, PCI_COMMAND_INTERRUPT_DISABLE); |
7856 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, reg); | | 7858 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, PCI_COMMAND_STATUS_REG, reg); |
7857 | intrstr = pci_intr_string(sc->sc_pct, sc->sc_pihp[0], intrbuf, | | 7859 | intrstr = pci_intr_string(sc->sc_pct, sc->sc_pihp[0], intrbuf, |
7858 | sizeof(intrbuf)); | | 7860 | sizeof(intrbuf)); |
7859 | sc->sc_ih = pci_intr_establish_xname(sc->sc_pct, sc->sc_pihp[0], | | 7861 | sc->sc_ih = pci_intr_establish_xname(sc->sc_pct, sc->sc_pihp[0], |
7860 | IPL_NET, iwm_intr, sc, device_xname(self)); | | 7862 | IPL_NET, iwm_intr, sc, device_xname(self)); |
7861 | if (sc->sc_ih == NULL) { | | 7863 | if (sc->sc_ih == NULL) { |
7862 | aprint_error_dev(self, "can't establish interrupt"); | | 7864 | aprint_error_dev(self, "can't establish interrupt"); |
7863 | if (intrstr != NULL) | | 7865 | if (intrstr != NULL) |
7864 | aprint_error(" at %s", intrstr); | | 7866 | aprint_error(" at %s", intrstr); |
7865 | aprint_error("\n"); | | 7867 | aprint_error("\n"); |
7866 | return; | | 7868 | return; |
7867 | } | | 7869 | } |
7868 | aprint_normal_dev(self, "interrupting at %s\n", intrstr); | | 7870 | aprint_normal_dev(self, "interrupting at %s\n", intrstr); |
7869 | | | 7871 | |
7870 | sc->sc_wantresp = IWM_CMD_RESP_IDLE; | | 7872 | sc->sc_wantresp = IWM_CMD_RESP_IDLE; |
7871 | | | 7873 | |
7872 | sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); | | 7874 | sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); |
7873 | switch (PCI_PRODUCT(sc->sc_pciid)) { | | 7875 | switch (PCI_PRODUCT(sc->sc_pciid)) { |
7874 | case PCI_PRODUCT_INTEL_WIFI_LINK_3160_1: | | 7876 | case PCI_PRODUCT_INTEL_WIFI_LINK_3160_1: |
7875 | case PCI_PRODUCT_INTEL_WIFI_LINK_3160_2: | | 7877 | case PCI_PRODUCT_INTEL_WIFI_LINK_3160_2: |
7876 | sc->sc_fwname = "iwlwifi-3160-17.ucode"; | | 7878 | sc->sc_fwname = "iwlwifi-3160-17.ucode"; |
7877 | sc->host_interrupt_operation_mode = 1; | | 7879 | sc->host_interrupt_operation_mode = 1; |
7878 | sc->apmg_wake_up_wa = 1; | | 7880 | sc->apmg_wake_up_wa = 1; |
7879 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; | | 7881 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; |
7880 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; | | 7882 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; |
7881 | break; | | 7883 | break; |
7882 | case PCI_PRODUCT_INTEL_WIFI_LINK_3165_1: | | 7884 | case PCI_PRODUCT_INTEL_WIFI_LINK_3165_1: |
7883 | case PCI_PRODUCT_INTEL_WIFI_LINK_3165_2: | | 7885 | case PCI_PRODUCT_INTEL_WIFI_LINK_3165_2: |
7884 | sc->sc_fwname = "iwlwifi-7265D-22.ucode"; | | 7886 | sc->sc_fwname = "iwlwifi-7265D-22.ucode"; |
7885 | sc->host_interrupt_operation_mode = 0; | | 7887 | sc->host_interrupt_operation_mode = 0; |
7886 | sc->apmg_wake_up_wa = 1; | | 7888 | sc->apmg_wake_up_wa = 1; |
7887 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; | | 7889 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; |
7888 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; | | 7890 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; |
7889 | break; | | 7891 | break; |
7890 | case PCI_PRODUCT_INTEL_WIFI_LINK_3168: | | 7892 | case PCI_PRODUCT_INTEL_WIFI_LINK_3168: |
7891 | sc->sc_fwname = "iwlwifi-3168-22.ucode"; | | 7893 | sc->sc_fwname = "iwlwifi-3168-22.ucode"; |
7892 | sc->host_interrupt_operation_mode = 0; | | 7894 | sc->host_interrupt_operation_mode = 0; |
7893 | sc->apmg_wake_up_wa = 1; | | 7895 | sc->apmg_wake_up_wa = 1; |
7894 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; | | 7896 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; |
7895 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; | | 7897 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; |
7896 | break; | | 7898 | break; |
7897 | case PCI_PRODUCT_INTEL_WIFI_LINK_7260_1: | | 7899 | case PCI_PRODUCT_INTEL_WIFI_LINK_7260_1: |
7898 | case PCI_PRODUCT_INTEL_WIFI_LINK_7260_2: | | 7900 | case PCI_PRODUCT_INTEL_WIFI_LINK_7260_2: |
7899 | sc->sc_fwname = "iwlwifi-7260-17.ucode"; | | 7901 | sc->sc_fwname = "iwlwifi-7260-17.ucode"; |
7900 | sc->host_interrupt_operation_mode = 1; | | 7902 | sc->host_interrupt_operation_mode = 1; |
7901 | sc->apmg_wake_up_wa = 1; | | 7903 | sc->apmg_wake_up_wa = 1; |
7902 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; | | 7904 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; |
7903 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; | | 7905 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; |
7904 | break; | | 7906 | break; |
7905 | case PCI_PRODUCT_INTEL_WIFI_LINK_7265_1: | | 7907 | case PCI_PRODUCT_INTEL_WIFI_LINK_7265_1: |
7906 | case PCI_PRODUCT_INTEL_WIFI_LINK_7265_2: | | 7908 | case PCI_PRODUCT_INTEL_WIFI_LINK_7265_2: |
7907 | sc->sc_fwname = (sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK) == | | 7909 | sc->sc_fwname = (sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK) == |
7908 | IWM_CSR_HW_REV_TYPE_7265D ? | | 7910 | IWM_CSR_HW_REV_TYPE_7265D ? |
7909 | "iwlwifi-7265D-22.ucode": "iwlwifi-7265-17.ucode"; | | 7911 | "iwlwifi-7265D-22.ucode": "iwlwifi-7265-17.ucode"; |
7910 | sc->host_interrupt_operation_mode = 0; | | 7912 | sc->host_interrupt_operation_mode = 0; |
7911 | sc->apmg_wake_up_wa = 1; | | 7913 | sc->apmg_wake_up_wa = 1; |
7912 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; | | 7914 | sc->sc_device_family = IWM_DEVICE_FAMILY_7000; |
7913 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; | | 7915 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; |
7914 | break; | | 7916 | break; |
7915 | case PCI_PRODUCT_INTEL_WIFI_LINK_8260_1: | | 7917 | case PCI_PRODUCT_INTEL_WIFI_LINK_8260_1: |
7916 | case PCI_PRODUCT_INTEL_WIFI_LINK_8260_2: | | 7918 | case PCI_PRODUCT_INTEL_WIFI_LINK_8260_2: |
7917 | case PCI_PRODUCT_INTEL_WIFI_LINK_4165_1: | | 7919 | case PCI_PRODUCT_INTEL_WIFI_LINK_4165_1: |
7918 | case PCI_PRODUCT_INTEL_WIFI_LINK_4165_2: | | 7920 | case PCI_PRODUCT_INTEL_WIFI_LINK_4165_2: |
7919 | sc->sc_fwname = "iwlwifi-8000C-22.ucode"; | | 7921 | sc->sc_fwname = "iwlwifi-8000C-22.ucode"; |
7920 | sc->host_interrupt_operation_mode = 0; | | 7922 | sc->host_interrupt_operation_mode = 0; |
7921 | sc->apmg_wake_up_wa = 0; | | 7923 | sc->apmg_wake_up_wa = 0; |
7922 | sc->sc_device_family = IWM_DEVICE_FAMILY_8000; | | 7924 | sc->sc_device_family = IWM_DEVICE_FAMILY_8000; |
7923 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; | | 7925 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; |
7924 | break; | | 7926 | break; |
7925 | case PCI_PRODUCT_INTEL_WIFI_LINK_8265: | | 7927 | case PCI_PRODUCT_INTEL_WIFI_LINK_8265: |
7926 | sc->sc_fwname = "iwlwifi-8265-22.ucode"; | | 7928 | sc->sc_fwname = "iwlwifi-8265-22.ucode"; |
7927 | sc->host_interrupt_operation_mode = 0; | | 7929 | sc->host_interrupt_operation_mode = 0; |
7928 | sc->apmg_wake_up_wa = 0; | | 7930 | sc->apmg_wake_up_wa = 0; |
7929 | sc->sc_device_family = IWM_DEVICE_FAMILY_8000; | | 7931 | sc->sc_device_family = IWM_DEVICE_FAMILY_8000; |
7930 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; | | 7932 | sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; |
7931 | break; | | 7933 | break; |
7932 | default: | | 7934 | default: |
7933 | aprint_error_dev(self, "unknown product %#x", | | 7935 | aprint_error_dev(self, "unknown product %#x", |
7934 | PCI_PRODUCT(sc->sc_pciid)); | | 7936 | PCI_PRODUCT(sc->sc_pciid)); |
7935 | return; | | 7937 | return; |
7936 | } | | 7938 | } |
7937 | DPRINTF(("%s: firmware=%s\n", DEVNAME(sc), sc->sc_fwname)); | | 7939 | DPRINTF(("%s: firmware=%s\n", DEVNAME(sc), sc->sc_fwname)); |
7938 | | | 7940 | |
7939 | /* | | 7941 | /* |
7940 | * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have | | 7942 | * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have |
7941 | * changed, and now the revision step also includes bit 0-1 (no more | | 7943 | * changed, and now the revision step also includes bit 0-1 (no more |
7942 | * "dash" value). To keep hw_rev backwards compatible - we'll store it | | 7944 | * "dash" value). To keep hw_rev backwards compatible - we'll store it |
7943 | * in the old format. | | 7945 | * in the old format. |
7944 | */ | | 7946 | */ |
7945 | | | 7947 | |
7946 | if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) | | 7948 | if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) |
7947 | sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | | | 7949 | sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | |
7948 | (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); | | 7950 | (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); |
7949 | | | 7951 | |
7950 | if (iwm_prepare_card_hw(sc) != 0) { | | 7952 | if (iwm_prepare_card_hw(sc) != 0) { |
7951 | aprint_error_dev(sc->sc_dev, "could not initialize hardware\n"); | | 7953 | aprint_error_dev(sc->sc_dev, "could not initialize hardware\n"); |
7952 | return; | | 7954 | return; |
7953 | } | | 7955 | } |
7954 | | | 7956 | |
7955 | if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { | | 7957 | if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { |
7956 | uint32_t hw_step; | | 7958 | uint32_t hw_step; |
7957 | | | 7959 | |
7958 | /* | | 7960 | /* |
7959 | * In order to recognize C step the driver should read the | | 7961 | * In order to recognize C step the driver should read the |
7960 | * chip version id located at the AUX bus MISC address. | | 7962 | * chip version id located at the AUX bus MISC address. |
7961 | */ | | 7963 | */ |
7962 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, | | 7964 | IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, |
7963 | IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); | | 7965 | IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); |
7964 | DELAY(2); | | 7966 | DELAY(2); |
7965 | | | 7967 | |
7966 | err = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, | | 7968 | err = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, |
7967 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, | | 7969 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, |
7968 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, | | 7970 | IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, |
7969 | 25000); | | 7971 | 25000); |
7970 | if (!err) { | | 7972 | if (!err) { |
7971 | aprint_error_dev(sc->sc_dev, | | 7973 | aprint_error_dev(sc->sc_dev, |
7972 | "failed to wake up the nic\n"); | | 7974 | "failed to wake up the nic\n"); |
7973 | return; | | 7975 | return; |
7974 | } | | 7976 | } |
7975 | | | 7977 | |
7976 | if (iwm_nic_lock(sc)) { | | 7978 | if (iwm_nic_lock(sc)) { |
7977 | hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG); | | 7979 | hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG); |
7978 | hw_step |= IWM_ENABLE_WFPM; | | 7980 | hw_step |= IWM_ENABLE_WFPM; |
7979 | iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step); | | 7981 | iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step); |
7980 | hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG); | | 7982 | hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG); |
7981 | hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF; | | 7983 | hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF; |
7982 | if (hw_step == 0x3) | | 7984 | if (hw_step == 0x3) |
7983 | sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) | | | 7985 | sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) | |
7984 | (IWM_SILICON_C_STEP << 2); | | 7986 | (IWM_SILICON_C_STEP << 2); |
7985 | iwm_nic_unlock(sc); | | 7987 | iwm_nic_unlock(sc); |
7986 | } else { | | 7988 | } else { |
7987 | aprint_error_dev(sc->sc_dev, | | 7989 | aprint_error_dev(sc->sc_dev, |
7988 | "failed to lock the nic\n"); | | 7990 | "failed to lock the nic\n"); |
7989 | return; | | 7991 | return; |
7990 | } | | 7992 | } |
7991 | } | | 7993 | } |
7992 | | | 7994 | |
7993 | /* | | 7995 | /* |
7994 | * Allocate DMA memory for firmware transfers. | | 7996 | * Allocate DMA memory for firmware transfers. |
7995 | * Must be aligned on a 16-byte boundary. | | 7997 | * Must be aligned on a 16-byte boundary. |
7996 | */ | | 7998 | */ |
7997 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, sc->sc_fwdmasegsz, | | 7999 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, sc->sc_fwdmasegsz, |
7998 | 16); | | 8000 | 16); |
7999 | if (err) { | | 8001 | if (err) { |
8000 | aprint_error_dev(sc->sc_dev, | | 8002 | aprint_error_dev(sc->sc_dev, |
8001 | "could not allocate memory for firmware\n"); | | 8003 | "could not allocate memory for firmware\n"); |
8002 | return; | | 8004 | return; |
8003 | } | | 8005 | } |
8004 | | | 8006 | |
8005 | /* Allocate "Keep Warm" page, used internally by the card. */ | | 8007 | /* Allocate "Keep Warm" page, used internally by the card. */ |
8006 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); | | 8008 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); |
8007 | if (err) { | | 8009 | if (err) { |
8008 | aprint_error_dev(sc->sc_dev, | | 8010 | aprint_error_dev(sc->sc_dev, |
8009 | "could not allocate keep warm page\n"); | | 8011 | "could not allocate keep warm page\n"); |
8010 | goto fail1; | | 8012 | goto fail1; |
8011 | } | | 8013 | } |
8012 | | | 8014 | |
8013 | /* Allocate interrupt cause table (ICT).*/ | | 8015 | /* Allocate interrupt cause table (ICT).*/ |
8014 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, IWM_ICT_SIZE, | | 8016 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, IWM_ICT_SIZE, |
8015 | 1 << IWM_ICT_PADDR_SHIFT); | | 8017 | 1 << IWM_ICT_PADDR_SHIFT); |
8016 | if (err) { | | 8018 | if (err) { |
8017 | aprint_error_dev(sc->sc_dev, "could not allocate ICT table\n"); | | 8019 | aprint_error_dev(sc->sc_dev, "could not allocate ICT table\n"); |
8018 | goto fail2; | | 8020 | goto fail2; |
8019 | } | | 8021 | } |
8020 | | | 8022 | |
8021 | /* TX scheduler rings must be aligned on a 1KB boundary. */ | | 8023 | /* TX scheduler rings must be aligned on a 1KB boundary. */ |
8022 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, | | 8024 | err = iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, |
8023 | __arraycount(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); | | 8025 | __arraycount(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); |
8024 | if (err) { | | 8026 | if (err) { |
8025 | aprint_error_dev(sc->sc_dev, | | 8027 | aprint_error_dev(sc->sc_dev, |
8026 | "could not allocate TX scheduler rings\n"); | | 8028 | "could not allocate TX scheduler rings\n"); |
8027 | goto fail3; | | 8029 | goto fail3; |
8028 | } | | 8030 | } |
8029 | | | 8031 | |
8030 | for (txq_i = 0; txq_i < __arraycount(sc->txq); txq_i++) { | | 8032 | for (txq_i = 0; txq_i < __arraycount(sc->txq); txq_i++) { |
8031 | err = iwm_alloc_tx_ring(sc, &sc->txq[txq_i], txq_i); | | 8033 | err = iwm_alloc_tx_ring(sc, &sc->txq[txq_i], txq_i); |
8032 | if (err) { | | 8034 | if (err) { |
8033 | aprint_error_dev(sc->sc_dev, | | 8035 | aprint_error_dev(sc->sc_dev, |
8034 | "could not allocate TX ring %d\n", txq_i); | | 8036 | "could not allocate TX ring %d\n", txq_i); |
8035 | goto fail4; | | 8037 | goto fail4; |
8036 | } | | 8038 | } |
8037 | } | | 8039 | } |
8038 | | | 8040 | |
8039 | err = iwm_alloc_rx_ring(sc, &sc->rxq); | | 8041 | err = iwm_alloc_rx_ring(sc, &sc->rxq); |
8040 | if (err) { | | 8042 | if (err) { |
8041 | aprint_error_dev(sc->sc_dev, "could not allocate RX ring\n"); | | 8043 | aprint_error_dev(sc->sc_dev, "could not allocate RX ring\n"); |
8042 | goto fail5; | | 8044 | goto fail5; |
8043 | } | | 8045 | } |
8044 | | | 8046 | |
8045 | /* Clear pending interrupts. */ | | 8047 | /* Clear pending interrupts. */ |
8046 | IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff); | | 8048 | IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff); |
8047 | | | 8049 | |
8048 | if ((err = sysctl_createv(&sc->sc_clog, 0, NULL, &node, | | 8050 | if ((err = sysctl_createv(&sc->sc_clog, 0, NULL, &node, |
8049 | 0, CTLTYPE_NODE, device_xname(sc->sc_dev), | | 8051 | 0, CTLTYPE_NODE, device_xname(sc->sc_dev), |
8050 | SYSCTL_DESCR("iwm per-controller controls"), | | 8052 | SYSCTL_DESCR("iwm per-controller controls"), |
8051 | NULL, 0, NULL, 0, | | 8053 | NULL, 0, NULL, 0, |
8052 | CTL_HW, iwm_sysctl_root_num, CTL_CREATE, | | 8054 | CTL_HW, iwm_sysctl_root_num, CTL_CREATE, |
8053 | CTL_EOL)) != 0) { | | 8055 | CTL_EOL)) != 0) { |
8054 | aprint_normal_dev(sc->sc_dev, | | 8056 | aprint_normal_dev(sc->sc_dev, |
8055 | "couldn't create iwm per-controller sysctl node\n"); | | 8057 | "couldn't create iwm per-controller sysctl node\n"); |
8056 | } | | 8058 | } |
8057 | if (err == 0) { | | 8059 | if (err == 0) { |
8058 | int iwm_nodenum = node->sysctl_num; | | 8060 | int iwm_nodenum = node->sysctl_num; |
8059 | | | 8061 | |
8060 | /* Reload firmware sysctl node */ | | 8062 | /* Reload firmware sysctl node */ |
8061 | if ((err = sysctl_createv(&sc->sc_clog, 0, NULL, &node, | | 8063 | if ((err = sysctl_createv(&sc->sc_clog, 0, NULL, &node, |
8062 | CTLFLAG_READWRITE, CTLTYPE_INT, "fw_loaded", | | 8064 | CTLFLAG_READWRITE, CTLTYPE_INT, "fw_loaded", |
8063 | SYSCTL_DESCR("Reload firmware"), | | 8065 | SYSCTL_DESCR("Reload firmware"), |
8064 | iwm_sysctl_fw_loaded_handler, 0, (void *)sc, 0, | | 8066 | iwm_sysctl_fw_loaded_handler, 0, (void *)sc, 0, |
8065 | CTL_HW, iwm_sysctl_root_num, iwm_nodenum, CTL_CREATE, | | 8067 | CTL_HW, iwm_sysctl_root_num, iwm_nodenum, CTL_CREATE, |
8066 | CTL_EOL)) != 0) { | | 8068 | CTL_EOL)) != 0) { |
8067 | aprint_normal_dev(sc->sc_dev, | | 8069 | aprint_normal_dev(sc->sc_dev, |
8068 | "couldn't create load_fw sysctl node\n"); | | 8070 | "couldn't create load_fw sysctl node\n"); |
8069 | } | | 8071 | } |
8070 | } | | 8072 | } |
8071 | | | 8073 | |
8072 | /* | | 8074 | /* |
8073 | * Attach interface | | 8075 | * Attach interface |
8074 | */ | | 8076 | */ |
8075 | ic->ic_ifp = ifp; | | 8077 | ic->ic_ifp = ifp; |
8076 | ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ | | 8078 | ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ |
8077 | ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ | | 8079 | ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ |
8078 | ic->ic_state = IEEE80211_S_INIT; | | 8080 | ic->ic_state = IEEE80211_S_INIT; |
8079 | | | 8081 | |
8080 | /* Set device capabilities. */ | | 8082 | /* Set device capabilities. */ |
8081 | ic->ic_caps = | | 8083 | ic->ic_caps = |
8082 | IEEE80211_C_WEP | /* WEP */ | | 8084 | IEEE80211_C_WEP | /* WEP */ |
8083 | IEEE80211_C_WPA | /* 802.11i */ | | 8085 | IEEE80211_C_WPA | /* 802.11i */ |
8084 | #ifdef notyet | | 8086 | #ifdef notyet |
8085 | IEEE80211_C_SCANALL | /* device scans all channels at once */ | | 8087 | IEEE80211_C_SCANALL | /* device scans all channels at once */ |
8086 | IEEE80211_C_SCANALLBAND | /* device scans all bands at once */ | | 8088 | IEEE80211_C_SCANALLBAND | /* device scans all bands at once */ |
8087 | #endif | | 8089 | #endif |
8088 | IEEE80211_C_SHSLOT | /* short slot time supported */ | | 8090 | IEEE80211_C_SHSLOT | /* short slot time supported */ |
8089 | IEEE80211_C_SHPREAMBLE; /* short preamble supported */ | | 8091 | IEEE80211_C_SHPREAMBLE; /* short preamble supported */ |
8090 | | | 8092 | |
8091 | #ifndef IEEE80211_NO_HT | | 8093 | #ifndef IEEE80211_NO_HT |
8092 | ic->ic_htcaps = IEEE80211_HTCAP_SGI20; | | 8094 | ic->ic_htcaps = IEEE80211_HTCAP_SGI20; |
8093 | ic->ic_htxcaps = 0; | | 8095 | ic->ic_htxcaps = 0; |
8094 | ic->ic_txbfcaps = 0; | | 8096 | ic->ic_txbfcaps = 0; |
8095 | ic->ic_aselcaps = 0; | | 8097 | ic->ic_aselcaps = 0; |
8096 | ic->ic_ampdu_params = (IEEE80211_AMPDU_PARAM_SS_4 | 0x3 /* 64k */); | | 8098 | ic->ic_ampdu_params = (IEEE80211_AMPDU_PARAM_SS_4 | 0x3 /* 64k */); |
8097 | #endif | | 8099 | #endif |
8098 | | | 8100 | |
8099 | /* all hardware can do 2.4GHz band */ | | 8101 | /* all hardware can do 2.4GHz band */ |
8100 | ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; | | 8102 | ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; |
8101 | ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; | | 8103 | ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; |
8102 | | | 8104 | |
8103 | for (int i = 0; i < __arraycount(sc->sc_phyctxt); i++) { | | 8105 | for (int i = 0; i < __arraycount(sc->sc_phyctxt); i++) { |
8104 | sc->sc_phyctxt[i].id = i; | | 8106 | sc->sc_phyctxt[i].id = i; |
8105 | } | | 8107 | } |
8106 | | | 8108 | |
8107 | sc->sc_amrr.amrr_min_success_threshold = 1; | | 8109 | sc->sc_amrr.amrr_min_success_threshold = 1; |
8108 | sc->sc_amrr.amrr_max_success_threshold = 15; | | 8110 | sc->sc_amrr.amrr_max_success_threshold = 15; |
8109 | | | 8111 | |
8110 | /* IBSS channel undefined for now. */ | | 8112 | /* IBSS channel undefined for now. */ |
8111 | ic->ic_ibss_chan = &ic->ic_channels[1]; | | 8113 | ic->ic_ibss_chan = &ic->ic_channels[1]; |
8112 | | | 8114 | |
8113 | #if 0 | | 8115 | #if 0 |
8114 | ic->ic_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; | | 8116 | ic->ic_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; |
8115 | #endif | | 8117 | #endif |
8116 | | | 8118 | |
8117 | ifp->if_softc = sc; | | 8119 | ifp->if_softc = sc; |
8118 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | | 8120 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
8119 | ifp->if_init = iwm_init; | | 8121 | ifp->if_init = iwm_init; |
8120 | ifp->if_stop = iwm_stop; | | 8122 | ifp->if_stop = iwm_stop; |
8121 | ifp->if_ioctl = iwm_ioctl; | | 8123 | ifp->if_ioctl = iwm_ioctl; |
8122 | ifp->if_start = iwm_start; | | 8124 | ifp->if_start = iwm_start; |
8123 | ifp->if_watchdog = iwm_watchdog; | | 8125 | ifp->if_watchdog = iwm_watchdog; |
8124 | IFQ_SET_READY(&ifp->if_snd); | | 8126 | IFQ_SET_READY(&ifp->if_snd); |
8125 | memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ); | | 8127 | memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ); |
8126 | | | 8128 | |
8127 | err = if_initialize(ifp); | | 8129 | err = if_initialize(ifp); |
8128 | if (err != 0) { | | 8130 | if (err != 0) { |
8129 | aprint_error_dev(sc->sc_dev, "if_initialize failed(%d)\n", | | 8131 | aprint_error_dev(sc->sc_dev, "if_initialize failed(%d)\n", |
8130 | err); | | 8132 | err); |
8131 | goto fail6; | | 8133 | goto fail6; |
8132 | } | | 8134 | } |
8133 | #if 0 | | 8135 | #if 0 |
8134 | ieee80211_ifattach(ic); | | 8136 | ieee80211_ifattach(ic); |
8135 | #else | | 8137 | #else |
8136 | ether_ifattach(ifp, ic->ic_myaddr); /* XXX */ | | 8138 | ether_ifattach(ifp, NULL); /* XXX */ |
8137 | #endif | | 8139 | #endif |
8138 | /* Use common softint-based if_input */ | | 8140 | /* Use common softint-based if_input */ |
8139 | ifp->if_percpuq = if_percpuq_create(ifp); | | 8141 | ifp->if_percpuq = if_percpuq_create(ifp); |
8140 | if_register(ifp); | | 8142 | if_register(ifp); |
8141 | | | 8143 | |
8142 | callout_init(&sc->sc_calib_to, 0); | | 8144 | callout_init(&sc->sc_calib_to, 0); |
8143 | callout_setfunc(&sc->sc_calib_to, iwm_calib_timeout, sc); | | 8145 | callout_setfunc(&sc->sc_calib_to, iwm_calib_timeout, sc); |
8144 | callout_init(&sc->sc_led_blink_to, 0); | | 8146 | callout_init(&sc->sc_led_blink_to, 0); |
8145 | callout_setfunc(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc); | | 8147 | callout_setfunc(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc); |
8146 | #ifndef IEEE80211_NO_HT | | 8148 | #ifndef IEEE80211_NO_HT |
8147 | if (workqueue_create(&sc->sc_setratewq, "iwmsr", | | 8149 | if (workqueue_create(&sc->sc_setratewq, "iwmsr", |
8148 | iwm_setrates_task, sc, PRI_NONE, IPL_NET, 0)) | | 8150 | iwm_setrates_task, sc, PRI_NONE, IPL_NET, 0)) |
8149 | panic("%s: could not create workqueue: setrates", | | 8151 | panic("%s: could not create workqueue: setrates", |
8150 | device_xname(self)); | | 8152 | device_xname(self)); |
8151 | if (workqueue_create(&sc->sc_bawq, "iwmba", | | 8153 | if (workqueue_create(&sc->sc_bawq, "iwmba", |
8152 | iwm_ba_task, sc, PRI_NONE, IPL_NET, 0)) | | 8154 | iwm_ba_task, sc, PRI_NONE, IPL_NET, 0)) |
8153 | panic("%s: could not create workqueue: blockack", | | 8155 | panic("%s: could not create workqueue: blockack", |
8154 | device_xname(self)); | | 8156 | device_xname(self)); |
8155 | if (workqueue_create(&sc->sc_htprowq, "iwmhtpro", | | 8157 | if (workqueue_create(&sc->sc_htprowq, "iwmhtpro", |
8156 | iwm_htprot_task, sc, PRI_NONE, IPL_NET, 0)) | | 8158 | iwm_htprot_task, sc, PRI_NONE, IPL_NET, 0)) |
8157 | panic("%s: could not create workqueue: htprot", | | 8159 | panic("%s: could not create workqueue: htprot", |
8158 | device_xname(self)); | | 8160 | device_xname(self)); |
8159 | #endif | | 8161 | #endif |
8160 | | | 8162 | |
8161 | if (pmf_device_register(self, NULL, NULL)) | | 8163 | if (pmf_device_register(self, NULL, NULL)) |
8162 | pmf_class_network_register(self, ifp); | | 8164 | pmf_class_network_register(self, ifp); |
8163 | else | | 8165 | else |
8164 | aprint_error_dev(self, "couldn't establish power handler\n"); | | 8166 | aprint_error_dev(self, "couldn't establish power handler\n"); |
8165 | | | 8167 | |
8166 | /* | | 8168 | /* |
8167 | * We can't do normal attach before the file system is mounted | | 8169 | * We can't do normal attach before the file system is mounted |
8168 | * because we cannot read the MAC address without loading the | | 8170 | * because we cannot read the MAC address without loading the |
8169 | * firmware from disk. So we postpone until mountroot is done. | | 8171 | * firmware from disk. So we postpone until mountroot is done. |
8170 | * Notably, this will require a full driver unload/load cycle | | 8172 | * Notably, this will require a full driver unload/load cycle |
8171 | * (or reboot) in case the firmware is not present when the | | 8173 | * (or reboot) in case the firmware is not present when the |
8172 | * hook runs. | | 8174 | * hook runs. |
8173 | */ | | 8175 | */ |
8174 | config_mountroot(self, iwm_attach_hook); | | 8176 | config_mountroot(self, iwm_attach_hook); |
8175 | | | 8177 | |
8176 | return; | | 8178 | return; |
8177 | | | 8179 | |
8178 | fail6: iwm_free_rx_ring(sc, &sc->rxq); | | 8180 | fail6: iwm_free_rx_ring(sc, &sc->rxq); |
8179 | fail5: while (--txq_i >= 0) | | 8181 | fail5: while (--txq_i >= 0) |
8180 | iwm_free_tx_ring(sc, &sc->txq[txq_i]); | | 8182 | iwm_free_tx_ring(sc, &sc->txq[txq_i]); |
8181 | fail4: iwm_dma_contig_free(&sc->sched_dma); | | 8183 | fail4: iwm_dma_contig_free(&sc->sched_dma); |
8182 | fail3: if (sc->ict_dma.vaddr != NULL) | | 8184 | fail3: if (sc->ict_dma.vaddr != NULL) |
8183 | iwm_dma_contig_free(&sc->ict_dma); | | 8185 | iwm_dma_contig_free(&sc->ict_dma); |
8184 | fail2: iwm_dma_contig_free(&sc->kw_dma); | | 8186 | fail2: iwm_dma_contig_free(&sc->kw_dma); |
8185 | fail1: iwm_dma_contig_free(&sc->fw_dma); | | 8187 | fail1: iwm_dma_contig_free(&sc->fw_dma); |
8186 | } | | 8188 | } |
8187 | | | 8189 | |
8188 | void | | 8190 | void |
8189 | iwm_radiotap_attach(struct iwm_softc *sc) | | 8191 | iwm_radiotap_attach(struct iwm_softc *sc) |
8190 | { | | 8192 | { |
8191 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); | | 8193 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); |
8192 | | | 8194 | |
8193 | bpf_attach2(ifp, DLT_IEEE802_11_RADIO, | | 8195 | bpf_attach2(ifp, DLT_IEEE802_11_RADIO, |
8194 | sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN, | | 8196 | sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN, |
8195 | &sc->sc_drvbpf); | | 8197 | &sc->sc_drvbpf); |
8196 | | | 8198 | |
8197 | sc->sc_rxtap_len = sizeof sc->sc_rxtapu; | | 8199 | sc->sc_rxtap_len = sizeof sc->sc_rxtapu; |
8198 | sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); | | 8200 | sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); |
8199 | sc->sc_rxtap.wr_ihdr.it_present = htole32(IWM_RX_RADIOTAP_PRESENT); | | 8201 | sc->sc_rxtap.wr_ihdr.it_present = htole32(IWM_RX_RADIOTAP_PRESENT); |
8200 | | | 8202 | |
8201 | sc->sc_txtap_len = sizeof sc->sc_txtapu; | | 8203 | sc->sc_txtap_len = sizeof sc->sc_txtapu; |
8202 | sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); | | 8204 | sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); |
8203 | sc->sc_txtap.wt_ihdr.it_present = htole32(IWM_TX_RADIOTAP_PRESENT); | | 8205 | sc->sc_txtap.wt_ihdr.it_present = htole32(IWM_TX_RADIOTAP_PRESENT); |
8204 | } | | 8206 | } |
8205 | | | 8207 | |
8206 | #if 0 | | 8208 | #if 0 |
8207 | static void | | 8209 | static void |
8208 | iwm_init_task(void *arg) | | 8210 | iwm_init_task(void *arg) |
8209 | { | | 8211 | { |
8210 | struct iwm_softc *sc = arg; | | 8212 | struct iwm_softc *sc = arg; |
8211 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); | | 8213 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); |
8212 | int s; | | 8214 | int s; |
8213 | | | 8215 | |
8214 | rw_enter_write(&sc->ioctl_rwl); | | 8216 | rw_enter_write(&sc->ioctl_rwl); |
8215 | s = splnet(); | | 8217 | s = splnet(); |
8216 | | | 8218 | |
8217 | iwm_stop(ifp, 0); | | 8219 | iwm_stop(ifp, 0); |
8218 | if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP) | | 8220 | if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP) |
8219 | iwm_init(ifp); | | 8221 | iwm_init(ifp); |
8220 | | | 8222 | |
8221 | splx(s); | | 8223 | splx(s); |
8222 | rw_exit(&sc->ioctl_rwl); | | 8224 | rw_exit(&sc->ioctl_rwl); |
8223 | } | | 8225 | } |
8224 | | | 8226 | |
8225 | static void | | 8227 | static void |
8226 | iwm_wakeup(struct iwm_softc *sc) | | 8228 | iwm_wakeup(struct iwm_softc *sc) |
8227 | { | | 8229 | { |
8228 | pcireg_t reg; | | 8230 | pcireg_t reg; |
8229 | | | 8231 | |
8230 | /* Clear device-specific "PCI retry timeout" register (41h). */ | | 8232 | /* Clear device-specific "PCI retry timeout" register (41h). */ |
8231 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); | | 8233 | reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40); |
8232 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg & ~0xff00); | | 8234 | pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg & ~0xff00); |
8233 | | | 8235 | |
8234 | iwm_init_task(sc); | | 8236 | iwm_init_task(sc); |
8235 | } | | 8237 | } |
8236 | | | 8238 | |
8237 | static int | | 8239 | static int |
8238 | iwm_activate(device_t self, enum devact act) | | 8240 | iwm_activate(device_t self, enum devact act) |
8239 | { | | 8241 | { |
8240 | struct iwm_softc *sc = device_private(self); | | 8242 | struct iwm_softc *sc = device_private(self); |
8241 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); | | 8243 | struct ifnet *ifp = IC2IFP(&sc->sc_ic); |
8242 | | | 8244 | |
8243 | switch (act) { | | 8245 | switch (act) { |
8244 | case DVACT_DEACTIVATE: | | 8246 | case DVACT_DEACTIVATE: |
8245 | if (ifp->if_flags & IFF_RUNNING) | | 8247 | if (ifp->if_flags & IFF_RUNNING) |
8246 | iwm_stop(ifp, 0); | | 8248 | iwm_stop(ifp, 0); |
8247 | return 0; | | 8249 | return 0; |
8248 | default: | | 8250 | default: |
8249 | return EOPNOTSUPP; | | 8251 | return EOPNOTSUPP; |
8250 | } | | 8252 | } |
8251 | } | | 8253 | } |
8252 | #endif | | 8254 | #endif |
8253 | | | 8255 | |
8254 | CFATTACH_DECL_NEW(iwm, sizeof(struct iwm_softc), iwm_match, iwm_attach, | | 8256 | CFATTACH_DECL_NEW(iwm, sizeof(struct iwm_softc), iwm_match, iwm_attach, |
8255 | NULL, NULL); | | 8257 | NULL, NULL); |
8256 | | | 8258 | |
8257 | static int | | 8259 | static int |
8258 | iwm_sysctl_fw_loaded_handler(SYSCTLFN_ARGS) | | 8260 | iwm_sysctl_fw_loaded_handler(SYSCTLFN_ARGS) |
8259 | { | | 8261 | { |
8260 | struct sysctlnode node; | | 8262 | struct sysctlnode node; |
8261 | struct iwm_softc *sc; | | 8263 | struct iwm_softc *sc; |
8262 | int err, t; | | 8264 | int err, t; |
8263 | | | 8265 | |
8264 | node = *rnode; | | 8266 | node = *rnode; |
8265 | sc = node.sysctl_data; | | 8267 | sc = node.sysctl_data; |
8266 | t = ISSET(sc->sc_flags, IWM_FLAG_FW_LOADED) ? 1 : 0; | | 8268 | t = ISSET(sc->sc_flags, IWM_FLAG_FW_LOADED) ? 1 : 0; |
8267 | node.sysctl_data = &t; | | 8269 | node.sysctl_data = &t; |
8268 | err = sysctl_lookup(SYSCTLFN_CALL(&node)); | | 8270 | err = sysctl_lookup(SYSCTLFN_CALL(&node)); |
8269 | if (err || newp == NULL) | | 8271 | if (err || newp == NULL) |
8270 | return err; | | 8272 | return err; |
8271 | | | 8273 | |
8272 | if (t == 0) | | 8274 | if (t == 0) |
8273 | CLR(sc->sc_flags, IWM_FLAG_FW_LOADED); | | 8275 | CLR(sc->sc_flags, IWM_FLAG_FW_LOADED); |
8274 | return 0; | | 8276 | return 0; |
8275 | } | | 8277 | } |
8276 | | | 8278 | |
8277 | SYSCTL_SETUP(sysctl_iwm, "sysctl iwm(4) subtree setup") | | 8279 | SYSCTL_SETUP(sysctl_iwm, "sysctl iwm(4) subtree setup") |
8278 | { | | 8280 | { |
8279 | const struct sysctlnode *rnode; | | 8281 | const struct sysctlnode *rnode; |
8280 | #ifdef IWM_DEBUG | | 8282 | #ifdef IWM_DEBUG |
8281 | const struct sysctlnode *cnode; | | 8283 | const struct sysctlnode *cnode; |
8282 | #endif /* IWM_DEBUG */ | | 8284 | #endif /* IWM_DEBUG */ |
8283 | int rc; | | 8285 | int rc; |
8284 | | | 8286 | |
8285 | if ((rc = sysctl_createv(clog, 0, NULL, &rnode, | | 8287 | if ((rc = sysctl_createv(clog, 0, NULL, &rnode, |
8286 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "iwm", | | 8288 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "iwm", |
8287 | SYSCTL_DESCR("iwm global controls"), | | 8289 | SYSCTL_DESCR("iwm global controls"), |
8288 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) | | 8290 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) |
8289 | goto err; | | 8291 | goto err; |
8290 | | | 8292 | |
8291 | iwm_sysctl_root_num = rnode->sysctl_num; | | 8293 | iwm_sysctl_root_num = rnode->sysctl_num; |
8292 | | | 8294 | |
8293 | #ifdef IWM_DEBUG | | 8295 | #ifdef IWM_DEBUG |
8294 | /* control debugging printfs */ | | 8296 | /* control debugging printfs */ |
8295 | if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, | | 8297 | if ((rc = sysctl_createv(clog, 0, &rnode, &cnode, |
8296 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, | | 8298 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, |
8297 | "debug", SYSCTL_DESCR("Enable debugging output"), | | 8299 | "debug", SYSCTL_DESCR("Enable debugging output"), |
8298 | NULL, 0, &iwm_debug, 0, CTL_CREATE, CTL_EOL)) != 0) | | 8300 | NULL, 0, &iwm_debug, 0, CTL_CREATE, CTL_EOL)) != 0) |
8299 | goto err; | | 8301 | goto err; |
8300 | #endif /* IWM_DEBUG */ | | 8302 | #endif /* IWM_DEBUG */ |
8301 | | | 8303 | |
8302 | return; | | 8304 | return; |
8303 | | | 8305 | |
8304 | err: | | 8306 | err: |
8305 | aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); | | 8307 | aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); |
8306 | } | | 8308 | } |