diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-07 18:37:24 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-07 18:37:24 -0500 |
commit | 7c7758f99d39d529a64d4f60d22129bbf2f16d74 (patch) | |
tree | 8847b5e56812fe4c4c812cfffc78e391a91f4ebe /drivers/net | |
parent | 67acd8b4b7a3f1b183ae358e1dfdb8a80e170736 (diff) | |
parent | 8a70da82edc50aa7a4b54864babf2d72538ba1bb (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (123 commits)
wimax/i2400m: add CREDITS and MAINTAINERS entries
wimax: export linux/wimax.h and linux/wimax/i2400m.h with headers_install
i2400m: Makefile and Kconfig
i2400m/SDIO: TX and RX path backends
i2400m/SDIO: firmware upload backend
i2400m/SDIO: probe/disconnect, dev init/shutdown and reset backends
i2400m/SDIO: header for the SDIO subdriver
i2400m/USB: TX and RX path backends
i2400m/USB: firmware upload backend
i2400m/USB: probe/disconnect, dev init/shutdown and reset backends
i2400m/USB: header for the USB bus driver
i2400m: debugfs controls
i2400m: various functions for device management
i2400m: RX and TX data/control paths
i2400m: firmware loading and bootrom initialization
i2400m: linkage to the networking stack
i2400m: Generic probe/disconnect, reset and message passing
i2400m: host/device procotol and core driver definitions
i2400m: documentation and instructions for usage
wimax: Makefile, Kconfig and docbook linkage for the stack
...
Diffstat (limited to 'drivers/net')
31 files changed, 9949 insertions, 9 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9a18270c1081..97ea7c60e002 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
@@ -2614,6 +2614,8 @@ source "drivers/net/tokenring/Kconfig" | |||
2614 | 2614 | ||
2615 | source "drivers/net/wireless/Kconfig" | 2615 | source "drivers/net/wireless/Kconfig" |
2616 | 2616 | ||
2617 | source "drivers/net/wimax/Kconfig" | ||
2618 | |||
2617 | source "drivers/net/usb/Kconfig" | 2619 | source "drivers/net/usb/Kconfig" |
2618 | 2620 | ||
2619 | source "drivers/net/pcmcia/Kconfig" | 2621 | source "drivers/net/pcmcia/Kconfig" |
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e5c34b464211..a3c5c002f224 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile | |||
@@ -263,3 +263,4 @@ obj-$(CONFIG_NIU) += niu.o | |||
263 | obj-$(CONFIG_VIRTIO_NET) += virtio_net.o | 263 | obj-$(CONFIG_VIRTIO_NET) += virtio_net.o |
264 | obj-$(CONFIG_SFC) += sfc/ | 264 | obj-$(CONFIG_SFC) += sfc/ |
265 | 265 | ||
266 | obj-$(CONFIG_WIMAX) += wimax/ | ||
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index 2ee034f70d1c..3073ca25a0b0 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c | |||
@@ -283,9 +283,9 @@ static int kaweth_control(struct kaweth_device *kaweth, | |||
283 | 283 | ||
284 | dr->bRequestType= requesttype; | 284 | dr->bRequestType= requesttype; |
285 | dr->bRequest = request; | 285 | dr->bRequest = request; |
286 | dr->wValue = cpu_to_le16p(&value); | 286 | dr->wValue = cpu_to_le16(value); |
287 | dr->wIndex = cpu_to_le16p(&index); | 287 | dr->wIndex = cpu_to_le16(index); |
288 | dr->wLength = cpu_to_le16p(&size); | 288 | dr->wLength = cpu_to_le16(size); |
289 | 289 | ||
290 | return kaweth_internal_control_msg(kaweth->dev, | 290 | return kaweth_internal_control_msg(kaweth->dev, |
291 | pipe, | 291 | pipe, |
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 166880c113d6..d9241f1c0803 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c | |||
@@ -150,8 +150,8 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, | |||
150 | pegasus->dr.bRequestType = PEGASUS_REQT_READ; | 150 | pegasus->dr.bRequestType = PEGASUS_REQT_READ; |
151 | pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS; | 151 | pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS; |
152 | pegasus->dr.wValue = cpu_to_le16(0); | 152 | pegasus->dr.wValue = cpu_to_le16(0); |
153 | pegasus->dr.wIndex = cpu_to_le16p(&indx); | 153 | pegasus->dr.wIndex = cpu_to_le16(indx); |
154 | pegasus->dr.wLength = cpu_to_le16p(&size); | 154 | pegasus->dr.wLength = cpu_to_le16(size); |
155 | pegasus->ctrl_urb->transfer_buffer_length = size; | 155 | pegasus->ctrl_urb->transfer_buffer_length = size; |
156 | 156 | ||
157 | usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, | 157 | usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, |
@@ -208,8 +208,8 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size, | |||
208 | pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; | 208 | pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; |
209 | pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; | 209 | pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; |
210 | pegasus->dr.wValue = cpu_to_le16(0); | 210 | pegasus->dr.wValue = cpu_to_le16(0); |
211 | pegasus->dr.wIndex = cpu_to_le16p(&indx); | 211 | pegasus->dr.wIndex = cpu_to_le16(indx); |
212 | pegasus->dr.wLength = cpu_to_le16p(&size); | 212 | pegasus->dr.wLength = cpu_to_le16(size); |
213 | pegasus->ctrl_urb->transfer_buffer_length = size; | 213 | pegasus->ctrl_urb->transfer_buffer_length = size; |
214 | 214 | ||
215 | usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, | 215 | usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, |
@@ -261,7 +261,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data) | |||
261 | pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; | 261 | pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; |
262 | pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; | 262 | pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; |
263 | pegasus->dr.wValue = cpu_to_le16(data); | 263 | pegasus->dr.wValue = cpu_to_le16(data); |
264 | pegasus->dr.wIndex = cpu_to_le16p(&indx); | 264 | pegasus->dr.wIndex = cpu_to_le16(indx); |
265 | pegasus->dr.wLength = cpu_to_le16(1); | 265 | pegasus->dr.wLength = cpu_to_le16(1); |
266 | pegasus->ctrl_urb->transfer_buffer_length = 1; | 266 | pegasus->ctrl_urb->transfer_buffer_length = 1; |
267 | 267 | ||
@@ -476,7 +476,7 @@ static inline void get_node_id(pegasus_t * pegasus, __u8 * id) | |||
476 | 476 | ||
477 | for (i = 0; i < 3; i++) { | 477 | for (i = 0; i < 3; i++) { |
478 | read_eprom_word(pegasus, i, &w16); | 478 | read_eprom_word(pegasus, i, &w16); |
479 | ((__le16 *) id)[i] = cpu_to_le16p(&w16); | 479 | ((__le16 *) id)[i] = cpu_to_le16(w16); |
480 | } | 480 | } |
481 | } | 481 | } |
482 | 482 | ||
diff --git a/drivers/net/wimax/Kconfig b/drivers/net/wimax/Kconfig new file mode 100644 index 000000000000..565018ec1e3b --- /dev/null +++ b/drivers/net/wimax/Kconfig | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # WiMAX LAN device drivers configuration | ||
3 | # | ||
4 | |||
5 | |||
6 | comment "Enable WiMAX (Networking options) to see the WiMAX drivers" | ||
7 | depends on WIMAX = n | ||
8 | |||
9 | if WIMAX | ||
10 | |||
11 | menu "WiMAX Wireless Broadband devices" | ||
12 | |||
13 | source "drivers/net/wimax/i2400m/Kconfig" | ||
14 | |||
15 | endmenu | ||
16 | |||
17 | endif | ||
diff --git a/drivers/net/wimax/Makefile b/drivers/net/wimax/Makefile new file mode 100644 index 000000000000..992bc02bc016 --- /dev/null +++ b/drivers/net/wimax/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | |||
2 | obj-$(CONFIG_WIMAX_I2400M) += i2400m/ | ||
3 | |||
4 | # (from Sam Ravnborg) force kbuild to create built-in.o | ||
5 | obj- := dummy.o | ||
diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig new file mode 100644 index 000000000000..d623b3d99a4b --- /dev/null +++ b/drivers/net/wimax/i2400m/Kconfig | |||
@@ -0,0 +1,49 @@ | |||
1 | |||
2 | config WIMAX_I2400M | ||
3 | tristate | ||
4 | depends on WIMAX | ||
5 | select FW_LOADER | ||
6 | |||
7 | comment "Enable USB support to see WiMAX USB drivers" | ||
8 | depends on USB = n | ||
9 | |||
10 | comment "Enable MMC support to see WiMAX SDIO drivers" | ||
11 | depends on MMC = n | ||
12 | |||
13 | config WIMAX_I2400M_USB | ||
14 | tristate "Intel Wireless WiMAX Connection 2400 over USB (including 5x50)" | ||
15 | depends on WIMAX && USB | ||
16 | select WIMAX_I2400M | ||
17 | help | ||
18 | Select if you have a device based on the Intel WiMAX | ||
19 | Connection 2400 over USB (like any of the Intel Wireless | ||
20 | WiMAX/WiFi Link 5x50 series). | ||
21 | |||
22 | If unsure, it is safe to select M (module). | ||
23 | |||
24 | config WIMAX_I2400M_SDIO | ||
25 | tristate "Intel Wireless WiMAX Connection 2400 over SDIO" | ||
26 | depends on WIMAX && MMC | ||
27 | select WIMAX_I2400M | ||
28 | help | ||
29 | Select if you have a device based on the Intel WiMAX | ||
30 | Connection 2400 over SDIO. | ||
31 | |||
32 | If unsure, it is safe to select M (module). | ||
33 | |||
34 | config WIMAX_I2400M_DEBUG_LEVEL | ||
35 | int "WiMAX i2400m debug level" | ||
36 | depends on WIMAX_I2400M | ||
37 | default 8 | ||
38 | help | ||
39 | |||
40 | Select the maximum debug verbosity level to be compiled into | ||
41 | the WiMAX i2400m driver code. | ||
42 | |||
43 | By default, this is disabled at runtime and can be | ||
44 | selectively enabled at runtime for different parts of the | ||
45 | code using the sysfs debug-levels file. | ||
46 | |||
47 | If set at zero, this will compile out all the debug code. | ||
48 | |||
49 | It is recommended that it is left at 8. | ||
diff --git a/drivers/net/wimax/i2400m/Makefile b/drivers/net/wimax/i2400m/Makefile new file mode 100644 index 000000000000..1696e936cf5a --- /dev/null +++ b/drivers/net/wimax/i2400m/Makefile | |||
@@ -0,0 +1,29 @@ | |||
1 | |||
2 | obj-$(CONFIG_WIMAX_I2400M) += i2400m.o | ||
3 | obj-$(CONFIG_WIMAX_I2400M_USB) += i2400m-usb.o | ||
4 | obj-$(CONFIG_WIMAX_I2400M_SDIO) += i2400m-sdio.o | ||
5 | |||
6 | i2400m-y := \ | ||
7 | control.o \ | ||
8 | driver.o \ | ||
9 | fw.o \ | ||
10 | op-rfkill.o \ | ||
11 | netdev.o \ | ||
12 | tx.o \ | ||
13 | rx.o | ||
14 | |||
15 | i2400m-$(CONFIG_DEBUG_FS) += debugfs.o | ||
16 | |||
17 | i2400m-usb-y := \ | ||
18 | usb-fw.o \ | ||
19 | usb-notif.o \ | ||
20 | usb-tx.o \ | ||
21 | usb-rx.o \ | ||
22 | usb.o | ||
23 | |||
24 | |||
25 | i2400m-sdio-y := \ | ||
26 | sdio.o \ | ||
27 | sdio-tx.o \ | ||
28 | sdio-fw.o \ | ||
29 | sdio-rx.o | ||
diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c new file mode 100644 index 000000000000..d3d37fed6893 --- /dev/null +++ b/drivers/net/wimax/i2400m/control.c | |||
@@ -0,0 +1,1291 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Miscellaneous control functions for managing the device | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
37 | * - Initial implementation | ||
38 | * | ||
39 | * This is a collection of functions used to control the device (plus | ||
40 | * a few helpers). | ||
41 | * | ||
42 | * There are utilities for handling TLV buffers, hooks on the device's | ||
43 | * reports to act on device changes of state [i2400m_report_hook()], | ||
44 | * on acks to commands [i2400m_msg_ack_hook()], a helper for sending | ||
45 | * commands to the device and blocking until a reply arrives | ||
46 | * [i2400m_msg_to_dev()], a few high level commands for manipulating | ||
47 | * the device state, powersving mode and configuration plus the | ||
48 | * routines to setup the device once communication is stablished with | ||
49 | * it [i2400m_dev_initialize()]. | ||
50 | * | ||
51 | * ROADMAP | ||
52 | * | ||
53 | * i2400m_dev_initalize() Called by i2400m_dev_start() | ||
54 | * i2400m_set_init_config() | ||
55 | * i2400m_firmware_check() | ||
56 | * i2400m_cmd_get_state() | ||
57 | * i2400m_dev_shutdown() Called by i2400m_dev_stop() | ||
58 | * i2400m->bus_reset() | ||
59 | * | ||
60 | * i2400m_{cmd,get,set}_*() | ||
61 | * i2400m_msg_to_dev() | ||
62 | * i2400m_msg_check_status() | ||
63 | * | ||
64 | * i2400m_report_hook() Called on reception of an event | ||
65 | * i2400m_report_state_hook() | ||
66 | * i2400m_tlv_buffer_walk() | ||
67 | * i2400m_tlv_match() | ||
68 | * i2400m_report_tlv_system_state() | ||
69 | * i2400m_report_tlv_rf_switches_status() | ||
70 | * i2400m_report_tlv_media_status() | ||
71 | * i2400m_cmd_enter_powersave() | ||
72 | * | ||
73 | * i2400m_msg_ack_hook() Called on reception of a reply to a | ||
74 | * command, get or set | ||
75 | */ | ||
76 | |||
77 | #include <stdarg.h> | ||
78 | #include "i2400m.h" | ||
79 | #include <linux/kernel.h> | ||
80 | #include <linux/wimax/i2400m.h> | ||
81 | |||
82 | |||
83 | #define D_SUBMODULE control | ||
84 | #include "debug-levels.h" | ||
85 | |||
86 | |||
87 | /* | ||
88 | * Return if a TLV is of a give type and size | ||
89 | * | ||
90 | * @tlv_hdr: pointer to the TLV | ||
91 | * @tlv_type: type of the TLV we are looking for | ||
92 | * @tlv_size: expected size of the TLV we are looking for (if -1, | ||
93 | * don't check the size). This includes the header | ||
94 | * Returns: 0 if the TLV matches | ||
95 | * < 0 if it doesn't match at all | ||
96 | * > 0 total TLV + payload size, if the type matches, but not | ||
97 | * the size | ||
98 | */ | ||
99 | static | ||
100 | ssize_t i2400m_tlv_match(const struct i2400m_tlv_hdr *tlv, | ||
101 | enum i2400m_tlv tlv_type, ssize_t tlv_size) | ||
102 | { | ||
103 | if (le16_to_cpu(tlv->type) != tlv_type) /* Not our type? skip */ | ||
104 | return -1; | ||
105 | if (tlv_size != -1 | ||
106 | && le16_to_cpu(tlv->length) + sizeof(*tlv) != tlv_size) { | ||
107 | size_t size = le16_to_cpu(tlv->length) + sizeof(*tlv); | ||
108 | printk(KERN_WARNING "W: tlv type 0x%x mismatched because of " | ||
109 | "size (got %zu vs %zu expected)\n", | ||
110 | tlv_type, size, tlv_size); | ||
111 | return size; | ||
112 | } | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | |||
117 | /* | ||
118 | * Given a buffer of TLVs, iterate over them | ||
119 | * | ||
120 | * @i2400m: device instance | ||
121 | * @tlv_buf: pointer to the beginning of the TLV buffer | ||
122 | * @buf_size: buffer size in bytes | ||
123 | * @tlv_pos: seek position; this is assumed to be a pointer returned | ||
124 | * by i2400m_tlv_buffer_walk() [and thus, validated]. The | ||
125 | * TLV returned will be the one following this one. | ||
126 | * | ||
127 | * Usage: | ||
128 | * | ||
129 | * tlv_itr = NULL; | ||
130 | * while (tlv_itr = i2400m_tlv_buffer_walk(i2400m, buf, size, tlv_itr)) { | ||
131 | * ... | ||
132 | * // Do stuff with tlv_itr, DON'T MODIFY IT | ||
133 | * ... | ||
134 | * } | ||
135 | */ | ||
136 | static | ||
137 | const struct i2400m_tlv_hdr *i2400m_tlv_buffer_walk( | ||
138 | struct i2400m *i2400m, | ||
139 | const void *tlv_buf, size_t buf_size, | ||
140 | const struct i2400m_tlv_hdr *tlv_pos) | ||
141 | { | ||
142 | struct device *dev = i2400m_dev(i2400m); | ||
143 | const struct i2400m_tlv_hdr *tlv_top = tlv_buf + buf_size; | ||
144 | size_t offset, length, avail_size; | ||
145 | unsigned type; | ||
146 | |||
147 | if (tlv_pos == NULL) /* Take the first one? */ | ||
148 | tlv_pos = tlv_buf; | ||
149 | else /* Nope, the next one */ | ||
150 | tlv_pos = (void *) tlv_pos | ||
151 | + le16_to_cpu(tlv_pos->length) + sizeof(*tlv_pos); | ||
152 | if (tlv_pos == tlv_top) { /* buffer done */ | ||
153 | tlv_pos = NULL; | ||
154 | goto error_beyond_end; | ||
155 | } | ||
156 | if (tlv_pos > tlv_top) { | ||
157 | tlv_pos = NULL; | ||
158 | WARN_ON(1); | ||
159 | goto error_beyond_end; | ||
160 | } | ||
161 | offset = (void *) tlv_pos - (void *) tlv_buf; | ||
162 | avail_size = buf_size - offset; | ||
163 | if (avail_size < sizeof(*tlv_pos)) { | ||
164 | dev_err(dev, "HW BUG? tlv_buf %p [%zu bytes], tlv @%zu: " | ||
165 | "short header\n", tlv_buf, buf_size, offset); | ||
166 | goto error_short_header; | ||
167 | } | ||
168 | type = le16_to_cpu(tlv_pos->type); | ||
169 | length = le16_to_cpu(tlv_pos->length); | ||
170 | if (avail_size < sizeof(*tlv_pos) + length) { | ||
171 | dev_err(dev, "HW BUG? tlv_buf %p [%zu bytes], " | ||
172 | "tlv type 0x%04x @%zu: " | ||
173 | "short data (%zu bytes vs %zu needed)\n", | ||
174 | tlv_buf, buf_size, type, offset, avail_size, | ||
175 | sizeof(*tlv_pos) + length); | ||
176 | goto error_short_header; | ||
177 | } | ||
178 | error_short_header: | ||
179 | error_beyond_end: | ||
180 | return tlv_pos; | ||
181 | } | ||
182 | |||
183 | |||
184 | /* | ||
185 | * Find a TLV in a buffer of sequential TLVs | ||
186 | * | ||
187 | * @i2400m: device descriptor | ||
188 | * @tlv_hdr: pointer to the first TLV in the sequence | ||
189 | * @size: size of the buffer in bytes; all TLVs are assumed to fit | ||
190 | * fully in the buffer (otherwise we'll complain). | ||
191 | * @tlv_type: type of the TLV we are looking for | ||
192 | * @tlv_size: expected size of the TLV we are looking for (if -1, | ||
193 | * don't check the size). This includes the header | ||
194 | * | ||
195 | * Returns: NULL if the TLV is not found, otherwise a pointer to | ||
196 | * it. If the sizes don't match, an error is printed and NULL | ||
197 | * returned. | ||
198 | */ | ||
199 | static | ||
200 | const struct i2400m_tlv_hdr *i2400m_tlv_find( | ||
201 | struct i2400m *i2400m, | ||
202 | const struct i2400m_tlv_hdr *tlv_hdr, size_t size, | ||
203 | enum i2400m_tlv tlv_type, ssize_t tlv_size) | ||
204 | { | ||
205 | ssize_t match; | ||
206 | struct device *dev = i2400m_dev(i2400m); | ||
207 | const struct i2400m_tlv_hdr *tlv = NULL; | ||
208 | while ((tlv = i2400m_tlv_buffer_walk(i2400m, tlv_hdr, size, tlv))) { | ||
209 | match = i2400m_tlv_match(tlv, tlv_type, tlv_size); | ||
210 | if (match == 0) /* found it :) */ | ||
211 | break; | ||
212 | if (match > 0) | ||
213 | dev_warn(dev, "TLV type 0x%04x found with size " | ||
214 | "mismatch (%zu vs %zu needed)\n", | ||
215 | tlv_type, match, tlv_size); | ||
216 | } | ||
217 | return tlv; | ||
218 | } | ||
219 | |||
220 | |||
221 | static const struct | ||
222 | { | ||
223 | char *msg; | ||
224 | int errno; | ||
225 | } ms_to_errno[I2400M_MS_MAX] = { | ||
226 | [I2400M_MS_DONE_OK] = { "", 0 }, | ||
227 | [I2400M_MS_DONE_IN_PROGRESS] = { "", 0 }, | ||
228 | [I2400M_MS_INVALID_OP] = { "invalid opcode", -ENOSYS }, | ||
229 | [I2400M_MS_BAD_STATE] = { "invalid state", -EILSEQ }, | ||
230 | [I2400M_MS_ILLEGAL_VALUE] = { "illegal value", -EINVAL }, | ||
231 | [I2400M_MS_MISSING_PARAMS] = { "missing parameters", -ENOMSG }, | ||
232 | [I2400M_MS_VERSION_ERROR] = { "bad version", -EIO }, | ||
233 | [I2400M_MS_ACCESSIBILITY_ERROR] = { "accesibility error", -EIO }, | ||
234 | [I2400M_MS_BUSY] = { "busy", -EBUSY }, | ||
235 | [I2400M_MS_CORRUPTED_TLV] = { "corrupted TLV", -EILSEQ }, | ||
236 | [I2400M_MS_UNINITIALIZED] = { "not unitialized", -EILSEQ }, | ||
237 | [I2400M_MS_UNKNOWN_ERROR] = { "unknown error", -EIO }, | ||
238 | [I2400M_MS_PRODUCTION_ERROR] = { "production error", -EIO }, | ||
239 | [I2400M_MS_NO_RF] = { "no RF", -EIO }, | ||
240 | [I2400M_MS_NOT_READY_FOR_POWERSAVE] = | ||
241 | { "not ready for powersave", -EACCES }, | ||
242 | [I2400M_MS_THERMAL_CRITICAL] = { "thermal critical", -EL3HLT }, | ||
243 | }; | ||
244 | |||
245 | |||
246 | /* | ||
247 | * i2400m_msg_check_status - translate a message's status code | ||
248 | * | ||
249 | * @i2400m: device descriptor | ||
250 | * @l3l4_hdr: message header | ||
251 | * @strbuf: buffer to place a formatted error message (unless NULL). | ||
252 | * @strbuf_size: max amount of available space; larger messages will | ||
253 | * be truncated. | ||
254 | * | ||
255 | * Returns: errno code corresponding to the status code in @l3l4_hdr | ||
256 | * and a message in @strbuf describing the error. | ||
257 | */ | ||
258 | int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *l3l4_hdr, | ||
259 | char *strbuf, size_t strbuf_size) | ||
260 | { | ||
261 | int result; | ||
262 | enum i2400m_ms status = le16_to_cpu(l3l4_hdr->status); | ||
263 | const char *str; | ||
264 | |||
265 | if (status == 0) | ||
266 | return 0; | ||
267 | if (status > ARRAY_SIZE(ms_to_errno)) { | ||
268 | str = "unknown status code"; | ||
269 | result = -EBADR; | ||
270 | } else { | ||
271 | str = ms_to_errno[status].msg; | ||
272 | result = ms_to_errno[status].errno; | ||
273 | } | ||
274 | if (strbuf) | ||
275 | snprintf(strbuf, strbuf_size, "%s (%d)", str, status); | ||
276 | return result; | ||
277 | } | ||
278 | |||
279 | |||
280 | /* | ||
281 | * Act on a TLV System State reported by the device | ||
282 | * | ||
283 | * @i2400m: device descriptor | ||
284 | * @ss: validated System State TLV | ||
285 | */ | ||
286 | static | ||
287 | void i2400m_report_tlv_system_state(struct i2400m *i2400m, | ||
288 | const struct i2400m_tlv_system_state *ss) | ||
289 | { | ||
290 | struct device *dev = i2400m_dev(i2400m); | ||
291 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
292 | enum i2400m_system_state i2400m_state = le32_to_cpu(ss->state); | ||
293 | |||
294 | d_fnstart(3, dev, "(i2400m %p ss %p [%u])\n", i2400m, ss, i2400m_state); | ||
295 | |||
296 | if (unlikely(i2400m->ready == 0)) /* act if up */ | ||
297 | goto out; | ||
298 | if (i2400m->state != i2400m_state) { | ||
299 | i2400m->state = i2400m_state; | ||
300 | wake_up_all(&i2400m->state_wq); | ||
301 | } | ||
302 | switch (i2400m_state) { | ||
303 | case I2400M_SS_UNINITIALIZED: | ||
304 | case I2400M_SS_INIT: | ||
305 | case I2400M_SS_CONFIG: | ||
306 | case I2400M_SS_PRODUCTION: | ||
307 | wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); | ||
308 | break; | ||
309 | |||
310 | case I2400M_SS_RF_OFF: | ||
311 | case I2400M_SS_RF_SHUTDOWN: | ||
312 | wimax_state_change(wimax_dev, WIMAX_ST_RADIO_OFF); | ||
313 | break; | ||
314 | |||
315 | case I2400M_SS_READY: | ||
316 | case I2400M_SS_STANDBY: | ||
317 | case I2400M_SS_SLEEPACTIVE: | ||
318 | wimax_state_change(wimax_dev, WIMAX_ST_READY); | ||
319 | break; | ||
320 | |||
321 | case I2400M_SS_CONNECTING: | ||
322 | case I2400M_SS_WIMAX_CONNECTED: | ||
323 | wimax_state_change(wimax_dev, WIMAX_ST_READY); | ||
324 | break; | ||
325 | |||
326 | case I2400M_SS_SCAN: | ||
327 | case I2400M_SS_OUT_OF_ZONE: | ||
328 | wimax_state_change(wimax_dev, WIMAX_ST_SCANNING); | ||
329 | break; | ||
330 | |||
331 | case I2400M_SS_IDLE: | ||
332 | d_printf(1, dev, "entering BS-negotiated idle mode\n"); | ||
333 | case I2400M_SS_DISCONNECTING: | ||
334 | case I2400M_SS_DATA_PATH_CONNECTED: | ||
335 | wimax_state_change(wimax_dev, WIMAX_ST_CONNECTED); | ||
336 | break; | ||
337 | |||
338 | default: | ||
339 | /* Huh? just in case, shut it down */ | ||
340 | dev_err(dev, "HW BUG? unknown state %u: shutting down\n", | ||
341 | i2400m_state); | ||
342 | i2400m->bus_reset(i2400m, I2400M_RT_WARM); | ||
343 | break; | ||
344 | }; | ||
345 | out: | ||
346 | d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n", | ||
347 | i2400m, ss, i2400m_state); | ||
348 | } | ||
349 | |||
350 | |||
351 | /* | ||
352 | * Parse and act on a TLV Media Status sent by the device | ||
353 | * | ||
354 | * @i2400m: device descriptor | ||
355 | * @ms: validated Media Status TLV | ||
356 | * | ||
357 | * This will set the carrier up on down based on the device's link | ||
358 | * report. This is done asides of what the WiMAX stack does based on | ||
359 | * the device's state as sometimes we need to do a link-renew (the BS | ||
360 | * wants us to renew a DHCP lease, for example). | ||
361 | * | ||
362 | * In fact, doc says that everytime we get a link-up, we should do a | ||
363 | * DHCP negotiation... | ||
364 | */ | ||
365 | static | ||
366 | void i2400m_report_tlv_media_status(struct i2400m *i2400m, | ||
367 | const struct i2400m_tlv_media_status *ms) | ||
368 | { | ||
369 | struct device *dev = i2400m_dev(i2400m); | ||
370 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
371 | struct net_device *net_dev = wimax_dev->net_dev; | ||
372 | enum i2400m_media_status status = le32_to_cpu(ms->media_status); | ||
373 | |||
374 | d_fnstart(3, dev, "(i2400m %p ms %p [%u])\n", i2400m, ms, status); | ||
375 | |||
376 | if (unlikely(i2400m->ready == 0)) /* act if up */ | ||
377 | goto out; | ||
378 | switch (status) { | ||
379 | case I2400M_MEDIA_STATUS_LINK_UP: | ||
380 | netif_carrier_on(net_dev); | ||
381 | break; | ||
382 | case I2400M_MEDIA_STATUS_LINK_DOWN: | ||
383 | netif_carrier_off(net_dev); | ||
384 | break; | ||
385 | /* | ||
386 | * This is the network telling us we need to retrain the DHCP | ||
387 | * lease -- so far, we are trusting the WiMAX Network Service | ||
388 | * in user space to pick this up and poke the DHCP client. | ||
389 | */ | ||
390 | case I2400M_MEDIA_STATUS_LINK_RENEW: | ||
391 | netif_carrier_on(net_dev); | ||
392 | break; | ||
393 | default: | ||
394 | dev_err(dev, "HW BUG? unknown media status %u\n", | ||
395 | status); | ||
396 | }; | ||
397 | out: | ||
398 | d_fnend(3, dev, "(i2400m %p ms %p [%u]) = void\n", | ||
399 | i2400m, ms, status); | ||
400 | } | ||
401 | |||
402 | |||
403 | /* | ||
404 | * Parse a 'state report' and extract carrier on/off information | ||
405 | * | ||
406 | * @i2400m: device descriptor | ||
407 | * @l3l4_hdr: pointer to message; it has been already validated for | ||
408 | * consistent size. | ||
409 | * @size: size of the message (header + payload). The header length | ||
410 | * declaration is assumed to be congruent with @size (as in | ||
411 | * sizeof(*l3l4_hdr) + l3l4_hdr->length == size) | ||
412 | * | ||
413 | * Extract from the report state the system state TLV and infer from | ||
414 | * there if we have a carrier or not. Update our local state and tell | ||
415 | * netdev. | ||
416 | * | ||
417 | * When setting the carrier, it's fine to set OFF twice (for example), | ||
418 | * as netif_carrier_off() will not generate two OFF events (just on | ||
419 | * the transitions). | ||
420 | */ | ||
421 | static | ||
422 | void i2400m_report_state_hook(struct i2400m *i2400m, | ||
423 | const struct i2400m_l3l4_hdr *l3l4_hdr, | ||
424 | size_t size, const char *tag) | ||
425 | { | ||
426 | struct device *dev = i2400m_dev(i2400m); | ||
427 | const struct i2400m_tlv_hdr *tlv; | ||
428 | const struct i2400m_tlv_system_state *ss; | ||
429 | const struct i2400m_tlv_rf_switches_status *rfss; | ||
430 | const struct i2400m_tlv_media_status *ms; | ||
431 | size_t tlv_size = le16_to_cpu(l3l4_hdr->length); | ||
432 | |||
433 | d_fnstart(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s)\n", | ||
434 | i2400m, l3l4_hdr, size, tag); | ||
435 | tlv = NULL; | ||
436 | |||
437 | while ((tlv = i2400m_tlv_buffer_walk(i2400m, &l3l4_hdr->pl, | ||
438 | tlv_size, tlv))) { | ||
439 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_SYSTEM_STATE, | ||
440 | sizeof(*ss))) { | ||
441 | ss = container_of(tlv, typeof(*ss), hdr); | ||
442 | d_printf(2, dev, "%s: system state TLV " | ||
443 | "found (0x%04x), state 0x%08x\n", | ||
444 | tag, I2400M_TLV_SYSTEM_STATE, | ||
445 | le32_to_cpu(ss->state)); | ||
446 | i2400m_report_tlv_system_state(i2400m, ss); | ||
447 | } | ||
448 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_RF_STATUS, | ||
449 | sizeof(*rfss))) { | ||
450 | rfss = container_of(tlv, typeof(*rfss), hdr); | ||
451 | d_printf(2, dev, "%s: RF status TLV " | ||
452 | "found (0x%04x), sw 0x%02x hw 0x%02x\n", | ||
453 | tag, I2400M_TLV_RF_STATUS, | ||
454 | le32_to_cpu(rfss->sw_rf_switch), | ||
455 | le32_to_cpu(rfss->hw_rf_switch)); | ||
456 | i2400m_report_tlv_rf_switches_status(i2400m, rfss); | ||
457 | } | ||
458 | if (0 == i2400m_tlv_match(tlv, I2400M_TLV_MEDIA_STATUS, | ||
459 | sizeof(*ms))) { | ||
460 | ms = container_of(tlv, typeof(*ms), hdr); | ||
461 | d_printf(2, dev, "%s: Media Status TLV: %u\n", | ||
462 | tag, le32_to_cpu(ms->media_status)); | ||
463 | i2400m_report_tlv_media_status(i2400m, ms); | ||
464 | } | ||
465 | } | ||
466 | d_fnend(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s) = void\n", | ||
467 | i2400m, l3l4_hdr, size, tag); | ||
468 | } | ||
469 | |||
470 | |||
471 | /* | ||
472 | * i2400m_report_hook - (maybe) act on a report | ||
473 | * | ||
474 | * @i2400m: device descriptor | ||
475 | * @l3l4_hdr: pointer to message; it has been already validated for | ||
476 | * consistent size. | ||
477 | * @size: size of the message (header + payload). The header length | ||
478 | * declaration is assumed to be congruent with @size (as in | ||
479 | * sizeof(*l3l4_hdr) + l3l4_hdr->length == size) | ||
480 | * | ||
481 | * Extract information we might need (like carrien on/off) from a | ||
482 | * device report. | ||
483 | */ | ||
484 | void i2400m_report_hook(struct i2400m *i2400m, | ||
485 | const struct i2400m_l3l4_hdr *l3l4_hdr, size_t size) | ||
486 | { | ||
487 | struct device *dev = i2400m_dev(i2400m); | ||
488 | unsigned msg_type; | ||
489 | |||
490 | d_fnstart(3, dev, "(i2400m %p l3l4_hdr %p size %zu)\n", | ||
491 | i2400m, l3l4_hdr, size); | ||
492 | /* Chew on the message, we might need some information from | ||
493 | * here */ | ||
494 | msg_type = le16_to_cpu(l3l4_hdr->type); | ||
495 | switch (msg_type) { | ||
496 | case I2400M_MT_REPORT_STATE: /* carrier detection... */ | ||
497 | i2400m_report_state_hook(i2400m, | ||
498 | l3l4_hdr, size, "REPORT STATE"); | ||
499 | break; | ||
500 | /* If the device is ready for power save, then ask it to do | ||
501 | * it. */ | ||
502 | case I2400M_MT_REPORT_POWERSAVE_READY: /* zzzzz */ | ||
503 | if (l3l4_hdr->status == cpu_to_le16(I2400M_MS_DONE_OK)) { | ||
504 | d_printf(1, dev, "ready for powersave, requesting\n"); | ||
505 | i2400m_cmd_enter_powersave(i2400m); | ||
506 | } | ||
507 | break; | ||
508 | }; | ||
509 | d_fnend(3, dev, "(i2400m %p l3l4_hdr %p size %zu) = void\n", | ||
510 | i2400m, l3l4_hdr, size); | ||
511 | } | ||
512 | |||
513 | |||
514 | /* | ||
515 | * i2400m_msg_ack_hook - process cmd/set/get ack for internal status | ||
516 | * | ||
517 | * @i2400m: device descriptor | ||
518 | * @l3l4_hdr: pointer to message; it has been already validated for | ||
519 | * consistent size. | ||
520 | * @size: size of the message | ||
521 | * | ||
522 | * Extract information we might need from acks to commands and act on | ||
523 | * it. This is akin to i2400m_report_hook(). Note most of this | ||
524 | * processing should be done in the function that calls the | ||
525 | * command. This is here for some cases where it can't happen... | ||
526 | */ | ||
527 | void i2400m_msg_ack_hook(struct i2400m *i2400m, | ||
528 | const struct i2400m_l3l4_hdr *l3l4_hdr, size_t size) | ||
529 | { | ||
530 | int result; | ||
531 | struct device *dev = i2400m_dev(i2400m); | ||
532 | unsigned ack_type, ack_status; | ||
533 | char strerr[32]; | ||
534 | |||
535 | /* Chew on the message, we might need some information from | ||
536 | * here */ | ||
537 | ack_type = le16_to_cpu(l3l4_hdr->type); | ||
538 | ack_status = le16_to_cpu(l3l4_hdr->status); | ||
539 | switch (ack_type) { | ||
540 | case I2400M_MT_CMD_ENTER_POWERSAVE: | ||
541 | /* This is just left here for the sake of example, as | ||
542 | * the processing is done somewhere else. */ | ||
543 | if (0) { | ||
544 | result = i2400m_msg_check_status( | ||
545 | l3l4_hdr, strerr, sizeof(strerr)); | ||
546 | if (result >= 0) | ||
547 | d_printf(1, dev, "ready for power save: %zd\n", | ||
548 | size); | ||
549 | } | ||
550 | break; | ||
551 | }; | ||
552 | return; | ||
553 | } | ||
554 | |||
555 | |||
556 | /* | ||
557 | * i2400m_msg_size_check() - verify message size and header are congruent | ||
558 | * | ||
559 | * It is ok if the total message size is larger than the expected | ||
560 | * size, as there can be padding. | ||
561 | */ | ||
562 | int i2400m_msg_size_check(struct i2400m *i2400m, | ||
563 | const struct i2400m_l3l4_hdr *l3l4_hdr, | ||
564 | size_t msg_size) | ||
565 | { | ||
566 | int result; | ||
567 | struct device *dev = i2400m_dev(i2400m); | ||
568 | size_t expected_size; | ||
569 | d_fnstart(4, dev, "(i2400m %p l3l4_hdr %p msg_size %zu)\n", | ||
570 | i2400m, l3l4_hdr, msg_size); | ||
571 | if (msg_size < sizeof(*l3l4_hdr)) { | ||
572 | dev_err(dev, "bad size for message header " | ||
573 | "(expected at least %zu, got %zu)\n", | ||
574 | (size_t) sizeof(*l3l4_hdr), msg_size); | ||
575 | result = -EIO; | ||
576 | goto error_hdr_size; | ||
577 | } | ||
578 | expected_size = le16_to_cpu(l3l4_hdr->length) + sizeof(*l3l4_hdr); | ||
579 | if (msg_size < expected_size) { | ||
580 | dev_err(dev, "bad size for message code 0x%04x (expected %zu, " | ||
581 | "got %zu)\n", le16_to_cpu(l3l4_hdr->type), | ||
582 | expected_size, msg_size); | ||
583 | result = -EIO; | ||
584 | } else | ||
585 | result = 0; | ||
586 | error_hdr_size: | ||
587 | d_fnend(4, dev, | ||
588 | "(i2400m %p l3l4_hdr %p msg_size %zu) = %d\n", | ||
589 | i2400m, l3l4_hdr, msg_size, result); | ||
590 | return result; | ||
591 | } | ||
592 | |||
593 | |||
594 | |||
595 | /* | ||
596 | * Cancel a wait for a command ACK | ||
597 | * | ||
598 | * @i2400m: device descriptor | ||
599 | * @code: [negative] errno code to cancel with (don't use | ||
600 | * -EINPROGRESS) | ||
601 | * | ||
602 | * If there is an ack already filled out, free it. | ||
603 | */ | ||
604 | void i2400m_msg_to_dev_cancel_wait(struct i2400m *i2400m, int code) | ||
605 | { | ||
606 | struct sk_buff *ack_skb; | ||
607 | unsigned long flags; | ||
608 | |||
609 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
610 | ack_skb = i2400m->ack_skb; | ||
611 | if (ack_skb && !IS_ERR(ack_skb)) | ||
612 | kfree(ack_skb); | ||
613 | i2400m->ack_skb = ERR_PTR(code); | ||
614 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
615 | } | ||
616 | |||
617 | |||
618 | /** | ||
619 | * i2400m_msg_to_dev - Send a control message to the device and get a response | ||
620 | * | ||
621 | * @i2400m: device descriptor | ||
622 | * | ||
623 | * @msg_skb: an skb * | ||
624 | * | ||
625 | * @buf: pointer to the buffer containing the message to be sent; it | ||
626 | * has to start with a &struct i2400M_l3l4_hdr and then | ||
627 | * followed by the payload. Once this function returns, the | ||
628 | * buffer can be reused. | ||
629 | * | ||
630 | * @buf_len: buffer size | ||
631 | * | ||
632 | * Returns: | ||
633 | * | ||
634 | * Pointer to skb containing the ack message. You need to check the | ||
635 | * pointer with IS_ERR(), as it might be an error code. Error codes | ||
636 | * could happen because: | ||
637 | * | ||
638 | * - the message wasn't formatted correctly | ||
639 | * - couldn't send the message | ||
640 | * - failed waiting for a response | ||
641 | * - the ack message wasn't formatted correctly | ||
642 | * | ||
643 | * The returned skb has been allocated with wimax_msg_to_user_alloc(), | ||
644 | * it contains the reponse in a netlink attribute and is ready to be | ||
645 | * passed up to user space with wimax_msg_to_user_send(). To access | ||
646 | * the payload and its length, use wimax_msg_{data,len}() on the skb. | ||
647 | * | ||
648 | * The skb has to be freed with kfree_skb() once done. | ||
649 | * | ||
650 | * Description: | ||
651 | * | ||
652 | * This function delivers a message/command to the device and waits | ||
653 | * for an ack to be received. The format is described in | ||
654 | * linux/wimax/i2400m.h. In summary, a command/get/set is followed by an | ||
655 | * ack. | ||
656 | * | ||
657 | * This function will not check the ack status, that's left up to the | ||
658 | * caller. Once done with the ack skb, it has to be kfree_skb()ed. | ||
659 | * | ||
660 | * The i2400m handles only one message at the same time, thus we need | ||
661 | * the mutex to exclude other players. | ||
662 | * | ||
663 | * We write the message and then wait for an answer to come back. The | ||
664 | * RX path intercepts control messages and handles them in | ||
665 | * i2400m_rx_ctl(). Reports (notifications) are (maybe) processed | ||
666 | * locally and then forwarded (as needed) to user space on the WiMAX | ||
667 | * stack message pipe. Acks are saved and passed back to us through an | ||
668 | * skb in i2400m->ack_skb which is ready to be given to generic | ||
669 | * netlink if need be. | ||
670 | */ | ||
671 | struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m, | ||
672 | const void *buf, size_t buf_len) | ||
673 | { | ||
674 | int result; | ||
675 | struct device *dev = i2400m_dev(i2400m); | ||
676 | const struct i2400m_l3l4_hdr *msg_l3l4_hdr; | ||
677 | struct sk_buff *ack_skb; | ||
678 | const struct i2400m_l3l4_hdr *ack_l3l4_hdr; | ||
679 | size_t ack_len; | ||
680 | int ack_timeout; | ||
681 | unsigned msg_type; | ||
682 | unsigned long flags; | ||
683 | |||
684 | d_fnstart(3, dev, "(i2400m %p buf %p len %zu)\n", | ||
685 | i2400m, buf, buf_len); | ||
686 | |||
687 | if (i2400m->boot_mode) | ||
688 | return ERR_PTR(-ENODEV); | ||
689 | |||
690 | msg_l3l4_hdr = buf; | ||
691 | /* Check msg & payload consistency */ | ||
692 | result = i2400m_msg_size_check(i2400m, msg_l3l4_hdr, buf_len); | ||
693 | if (result < 0) | ||
694 | goto error_bad_msg; | ||
695 | msg_type = le16_to_cpu(msg_l3l4_hdr->type); | ||
696 | d_printf(1, dev, "CMD/GET/SET 0x%04x %zu bytes\n", | ||
697 | msg_type, buf_len); | ||
698 | d_dump(2, dev, buf, buf_len); | ||
699 | |||
700 | /* Setup the completion, ack_skb ("we are waiting") and send | ||
701 | * the message to the device */ | ||
702 | mutex_lock(&i2400m->msg_mutex); | ||
703 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
704 | i2400m->ack_skb = ERR_PTR(-EINPROGRESS); | ||
705 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
706 | init_completion(&i2400m->msg_completion); | ||
707 | result = i2400m_tx(i2400m, buf, buf_len, I2400M_PT_CTRL); | ||
708 | if (result < 0) { | ||
709 | dev_err(dev, "can't send message 0x%04x: %d\n", | ||
710 | le16_to_cpu(msg_l3l4_hdr->type), result); | ||
711 | goto error_tx; | ||
712 | } | ||
713 | |||
714 | /* Some commands take longer to execute because of crypto ops, | ||
715 | * so we give them some more leeway on timeout */ | ||
716 | switch (msg_type) { | ||
717 | case I2400M_MT_GET_TLS_OPERATION_RESULT: | ||
718 | case I2400M_MT_CMD_SEND_EAP_RESPONSE: | ||
719 | ack_timeout = 5 * HZ; | ||
720 | break; | ||
721 | default: | ||
722 | ack_timeout = HZ; | ||
723 | }; | ||
724 | |||
725 | /* The RX path in rx.c will put any response for this message | ||
726 | * in i2400m->ack_skb and wake us up. If we cancel the wait, | ||
727 | * we need to change the value of i2400m->ack_skb to something | ||
728 | * not -EINPROGRESS so RX knows there is no one waiting. */ | ||
729 | result = wait_for_completion_interruptible_timeout( | ||
730 | &i2400m->msg_completion, ack_timeout); | ||
731 | if (result == 0) { | ||
732 | dev_err(dev, "timeout waiting for reply to message 0x%04x\n", | ||
733 | msg_type); | ||
734 | result = -ETIMEDOUT; | ||
735 | i2400m_msg_to_dev_cancel_wait(i2400m, result); | ||
736 | goto error_wait_for_completion; | ||
737 | } else if (result < 0) { | ||
738 | dev_err(dev, "error waiting for reply to message 0x%04x: %d\n", | ||
739 | msg_type, result); | ||
740 | i2400m_msg_to_dev_cancel_wait(i2400m, result); | ||
741 | goto error_wait_for_completion; | ||
742 | } | ||
743 | |||
744 | /* Pull out the ack data from i2400m->ack_skb -- see if it is | ||
745 | * an error and act accordingly */ | ||
746 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
747 | ack_skb = i2400m->ack_skb; | ||
748 | if (IS_ERR(ack_skb)) | ||
749 | result = PTR_ERR(ack_skb); | ||
750 | else | ||
751 | result = 0; | ||
752 | i2400m->ack_skb = NULL; | ||
753 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
754 | if (result < 0) | ||
755 | goto error_ack_status; | ||
756 | ack_l3l4_hdr = wimax_msg_data_len(ack_skb, &ack_len); | ||
757 | |||
758 | /* Check the ack and deliver it if it is ok */ | ||
759 | result = i2400m_msg_size_check(i2400m, ack_l3l4_hdr, ack_len); | ||
760 | if (result < 0) { | ||
761 | dev_err(dev, "HW BUG? reply to message 0x%04x: %d\n", | ||
762 | msg_type, result); | ||
763 | goto error_bad_ack_len; | ||
764 | } | ||
765 | if (msg_type != le16_to_cpu(ack_l3l4_hdr->type)) { | ||
766 | dev_err(dev, "HW BUG? bad reply 0x%04x to message 0x%04x\n", | ||
767 | le16_to_cpu(ack_l3l4_hdr->type), msg_type); | ||
768 | result = -EIO; | ||
769 | goto error_bad_ack_type; | ||
770 | } | ||
771 | i2400m_msg_ack_hook(i2400m, ack_l3l4_hdr, ack_len); | ||
772 | mutex_unlock(&i2400m->msg_mutex); | ||
773 | d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %p\n", | ||
774 | i2400m, buf, buf_len, ack_skb); | ||
775 | return ack_skb; | ||
776 | |||
777 | error_bad_ack_type: | ||
778 | error_bad_ack_len: | ||
779 | kfree_skb(ack_skb); | ||
780 | error_ack_status: | ||
781 | error_wait_for_completion: | ||
782 | error_tx: | ||
783 | mutex_unlock(&i2400m->msg_mutex); | ||
784 | error_bad_msg: | ||
785 | d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %d\n", | ||
786 | i2400m, buf, buf_len, result); | ||
787 | return ERR_PTR(result); | ||
788 | } | ||
789 | |||
790 | |||
791 | /* | ||
792 | * Definitions for the Enter Power Save command | ||
793 | * | ||
794 | * The Enter Power Save command requests the device to go into power | ||
795 | * saving mode. The device will ack or nak the command depending on it | ||
796 | * being ready for it. If it acks, we tell the USB subsystem to | ||
797 | * | ||
798 | * As well, the device might request to go into power saving mode by | ||
799 | * sending a report (REPORT_POWERSAVE_READY), in which case, we issue | ||
800 | * this command. The hookups in the RX coder allow | ||
801 | */ | ||
802 | enum { | ||
803 | I2400M_WAKEUP_ENABLED = 0x01, | ||
804 | I2400M_WAKEUP_DISABLED = 0x02, | ||
805 | I2400M_TLV_TYPE_WAKEUP_MODE = 144, | ||
806 | }; | ||
807 | |||
808 | struct i2400m_cmd_enter_power_save { | ||
809 | struct i2400m_l3l4_hdr hdr; | ||
810 | struct i2400m_tlv_hdr tlv; | ||
811 | __le32 val; | ||
812 | } __attribute__((packed)); | ||
813 | |||
814 | |||
815 | /* | ||
816 | * Request entering power save | ||
817 | * | ||
818 | * This command is (mainly) executed when the device indicates that it | ||
819 | * is ready to go into powersave mode via a REPORT_POWERSAVE_READY. | ||
820 | */ | ||
821 | int i2400m_cmd_enter_powersave(struct i2400m *i2400m) | ||
822 | { | ||
823 | int result; | ||
824 | struct device *dev = i2400m_dev(i2400m); | ||
825 | struct sk_buff *ack_skb; | ||
826 | struct i2400m_cmd_enter_power_save *cmd; | ||
827 | char strerr[32]; | ||
828 | |||
829 | result = -ENOMEM; | ||
830 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
831 | if (cmd == NULL) | ||
832 | goto error_alloc; | ||
833 | cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_ENTER_POWERSAVE); | ||
834 | cmd->hdr.length = cpu_to_le16(sizeof(*cmd) - sizeof(cmd->hdr)); | ||
835 | cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION); | ||
836 | cmd->tlv.type = cpu_to_le16(I2400M_TLV_TYPE_WAKEUP_MODE); | ||
837 | cmd->tlv.length = cpu_to_le16(sizeof(cmd->val)); | ||
838 | cmd->val = cpu_to_le32(I2400M_WAKEUP_ENABLED); | ||
839 | |||
840 | ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); | ||
841 | result = PTR_ERR(ack_skb); | ||
842 | if (IS_ERR(ack_skb)) { | ||
843 | dev_err(dev, "Failed to issue 'Enter power save' command: %d\n", | ||
844 | result); | ||
845 | goto error_msg_to_dev; | ||
846 | } | ||
847 | result = i2400m_msg_check_status(wimax_msg_data(ack_skb), | ||
848 | strerr, sizeof(strerr)); | ||
849 | if (result == -EACCES) | ||
850 | d_printf(1, dev, "Cannot enter power save mode\n"); | ||
851 | else if (result < 0) | ||
852 | dev_err(dev, "'Enter power save' (0x%04x) command failed: " | ||
853 | "%d - %s\n", I2400M_MT_CMD_ENTER_POWERSAVE, | ||
854 | result, strerr); | ||
855 | else | ||
856 | d_printf(1, dev, "device ready to power save\n"); | ||
857 | kfree_skb(ack_skb); | ||
858 | error_msg_to_dev: | ||
859 | kfree(cmd); | ||
860 | error_alloc: | ||
861 | return result; | ||
862 | } | ||
863 | EXPORT_SYMBOL_GPL(i2400m_cmd_enter_powersave); | ||
864 | |||
865 | |||
866 | /* | ||
867 | * Definitions for getting device information | ||
868 | */ | ||
869 | enum { | ||
870 | I2400M_TLV_DETAILED_DEVICE_INFO = 140 | ||
871 | }; | ||
872 | |||
873 | /** | ||
874 | * i2400m_get_device_info - Query the device for detailed device information | ||
875 | * | ||
876 | * @i2400m: device descriptor | ||
877 | * | ||
878 | * Returns: an skb whose skb->data points to a 'struct | ||
879 | * i2400m_tlv_detailed_device_info'. When done, kfree_skb() it. The | ||
880 | * skb is *guaranteed* to contain the whole TLV data structure. | ||
881 | * | ||
882 | * On error, IS_ERR(skb) is true and ERR_PTR(skb) is the error | ||
883 | * code. | ||
884 | */ | ||
885 | struct sk_buff *i2400m_get_device_info(struct i2400m *i2400m) | ||
886 | { | ||
887 | int result; | ||
888 | struct device *dev = i2400m_dev(i2400m); | ||
889 | struct sk_buff *ack_skb; | ||
890 | struct i2400m_l3l4_hdr *cmd; | ||
891 | const struct i2400m_l3l4_hdr *ack; | ||
892 | size_t ack_len; | ||
893 | const struct i2400m_tlv_hdr *tlv; | ||
894 | const struct i2400m_tlv_detailed_device_info *ddi; | ||
895 | char strerr[32]; | ||
896 | |||
897 | ack_skb = ERR_PTR(-ENOMEM); | ||
898 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
899 | if (cmd == NULL) | ||
900 | goto error_alloc; | ||
901 | cmd->type = cpu_to_le16(I2400M_MT_GET_DEVICE_INFO); | ||
902 | cmd->length = 0; | ||
903 | cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); | ||
904 | |||
905 | ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); | ||
906 | if (IS_ERR(ack_skb)) { | ||
907 | dev_err(dev, "Failed to issue 'get device info' command: %ld\n", | ||
908 | PTR_ERR(ack_skb)); | ||
909 | goto error_msg_to_dev; | ||
910 | } | ||
911 | ack = wimax_msg_data_len(ack_skb, &ack_len); | ||
912 | result = i2400m_msg_check_status(ack, strerr, sizeof(strerr)); | ||
913 | if (result < 0) { | ||
914 | dev_err(dev, "'get device info' (0x%04x) command failed: " | ||
915 | "%d - %s\n", I2400M_MT_GET_DEVICE_INFO, result, | ||
916 | strerr); | ||
917 | goto error_cmd_failed; | ||
918 | } | ||
919 | tlv = i2400m_tlv_find(i2400m, ack->pl, ack_len - sizeof(*ack), | ||
920 | I2400M_TLV_DETAILED_DEVICE_INFO, sizeof(*ddi)); | ||
921 | if (tlv == NULL) { | ||
922 | dev_err(dev, "GET DEVICE INFO: " | ||
923 | "detailed device info TLV not found (0x%04x)\n", | ||
924 | I2400M_TLV_DETAILED_DEVICE_INFO); | ||
925 | result = -EIO; | ||
926 | goto error_no_tlv; | ||
927 | } | ||
928 | skb_pull(ack_skb, (void *) tlv - (void *) ack_skb->data); | ||
929 | error_msg_to_dev: | ||
930 | kfree(cmd); | ||
931 | error_alloc: | ||
932 | return ack_skb; | ||
933 | |||
934 | error_no_tlv: | ||
935 | error_cmd_failed: | ||
936 | kfree_skb(ack_skb); | ||
937 | kfree(cmd); | ||
938 | return ERR_PTR(result); | ||
939 | } | ||
940 | |||
941 | |||
942 | /* Firmware interface versions we support */ | ||
943 | enum { | ||
944 | I2400M_HDIv_MAJOR = 9, | ||
945 | I2400M_HDIv_MAJOR_2 = 8, | ||
946 | I2400M_HDIv_MINOR = 1, | ||
947 | }; | ||
948 | |||
949 | |||
950 | /** | ||
951 | * i2400m_firmware_check - check firmware versions are compatible with | ||
952 | * the driver | ||
953 | * | ||
954 | * @i2400m: device descriptor | ||
955 | * | ||
956 | * Returns: 0 if ok, < 0 errno code an error and a message in the | ||
957 | * kernel log. | ||
958 | * | ||
959 | * Long function, but quite simple; first chunk launches the command | ||
960 | * and double checks the reply for the right TLV. Then we process the | ||
961 | * TLV (where the meat is). | ||
962 | */ | ||
963 | int i2400m_firmware_check(struct i2400m *i2400m) | ||
964 | { | ||
965 | int result; | ||
966 | struct device *dev = i2400m_dev(i2400m); | ||
967 | struct sk_buff *ack_skb; | ||
968 | struct i2400m_l3l4_hdr *cmd; | ||
969 | const struct i2400m_l3l4_hdr *ack; | ||
970 | size_t ack_len; | ||
971 | const struct i2400m_tlv_hdr *tlv; | ||
972 | const struct i2400m_tlv_l4_message_versions *l4mv; | ||
973 | char strerr[32]; | ||
974 | unsigned major, minor, branch; | ||
975 | |||
976 | result = -ENOMEM; | ||
977 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
978 | if (cmd == NULL) | ||
979 | goto error_alloc; | ||
980 | cmd->type = cpu_to_le16(I2400M_MT_GET_LM_VERSION); | ||
981 | cmd->length = 0; | ||
982 | cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); | ||
983 | |||
984 | ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); | ||
985 | if (IS_ERR(ack_skb)) { | ||
986 | result = PTR_ERR(ack_skb); | ||
987 | dev_err(dev, "Failed to issue 'get lm version' command: %-d\n", | ||
988 | result); | ||
989 | goto error_msg_to_dev; | ||
990 | } | ||
991 | ack = wimax_msg_data_len(ack_skb, &ack_len); | ||
992 | result = i2400m_msg_check_status(ack, strerr, sizeof(strerr)); | ||
993 | if (result < 0) { | ||
994 | dev_err(dev, "'get lm version' (0x%04x) command failed: " | ||
995 | "%d - %s\n", I2400M_MT_GET_LM_VERSION, result, | ||
996 | strerr); | ||
997 | goto error_cmd_failed; | ||
998 | } | ||
999 | tlv = i2400m_tlv_find(i2400m, ack->pl, ack_len - sizeof(*ack), | ||
1000 | I2400M_TLV_L4_MESSAGE_VERSIONS, sizeof(*l4mv)); | ||
1001 | if (tlv == NULL) { | ||
1002 | dev_err(dev, "get lm version: TLV not found (0x%04x)\n", | ||
1003 | I2400M_TLV_L4_MESSAGE_VERSIONS); | ||
1004 | result = -EIO; | ||
1005 | goto error_no_tlv; | ||
1006 | } | ||
1007 | l4mv = container_of(tlv, typeof(*l4mv), hdr); | ||
1008 | major = le16_to_cpu(l4mv->major); | ||
1009 | minor = le16_to_cpu(l4mv->minor); | ||
1010 | branch = le16_to_cpu(l4mv->branch); | ||
1011 | result = -EINVAL; | ||
1012 | if (major != I2400M_HDIv_MAJOR | ||
1013 | && major != I2400M_HDIv_MAJOR_2) { | ||
1014 | dev_err(dev, "unsupported major fw interface version " | ||
1015 | "%u.%u.%u\n", major, minor, branch); | ||
1016 | goto error_bad_major; | ||
1017 | } | ||
1018 | if (major == I2400M_HDIv_MAJOR_2) | ||
1019 | dev_err(dev, "deprecated major fw interface version " | ||
1020 | "%u.%u.%u\n", major, minor, branch); | ||
1021 | result = 0; | ||
1022 | if (minor != I2400M_HDIv_MINOR) | ||
1023 | dev_warn(dev, "untested minor fw firmware version %u.%u.%u\n", | ||
1024 | major, minor, branch); | ||
1025 | error_bad_major: | ||
1026 | dev_info(dev, "firmware interface version %u.%u.%u\n", | ||
1027 | major, minor, branch); | ||
1028 | error_no_tlv: | ||
1029 | error_cmd_failed: | ||
1030 | kfree_skb(ack_skb); | ||
1031 | error_msg_to_dev: | ||
1032 | kfree(cmd); | ||
1033 | error_alloc: | ||
1034 | return result; | ||
1035 | } | ||
1036 | |||
1037 | |||
1038 | /* | ||
1039 | * Send an DoExitIdle command to the device to ask it to go out of | ||
1040 | * basestation-idle mode. | ||
1041 | * | ||
1042 | * @i2400m: device descriptor | ||
1043 | * | ||
1044 | * This starts a renegotiation with the basestation that might involve | ||
1045 | * another crypto handshake with user space. | ||
1046 | * | ||
1047 | * Returns: 0 if ok, < 0 errno code on error. | ||
1048 | */ | ||
1049 | int i2400m_cmd_exit_idle(struct i2400m *i2400m) | ||
1050 | { | ||
1051 | int result; | ||
1052 | struct device *dev = i2400m_dev(i2400m); | ||
1053 | struct sk_buff *ack_skb; | ||
1054 | struct i2400m_l3l4_hdr *cmd; | ||
1055 | char strerr[32]; | ||
1056 | |||
1057 | result = -ENOMEM; | ||
1058 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
1059 | if (cmd == NULL) | ||
1060 | goto error_alloc; | ||
1061 | cmd->type = cpu_to_le16(I2400M_MT_CMD_EXIT_IDLE); | ||
1062 | cmd->length = 0; | ||
1063 | cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); | ||
1064 | |||
1065 | ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); | ||
1066 | result = PTR_ERR(ack_skb); | ||
1067 | if (IS_ERR(ack_skb)) { | ||
1068 | dev_err(dev, "Failed to issue 'exit idle' command: %d\n", | ||
1069 | result); | ||
1070 | goto error_msg_to_dev; | ||
1071 | } | ||
1072 | result = i2400m_msg_check_status(wimax_msg_data(ack_skb), | ||
1073 | strerr, sizeof(strerr)); | ||
1074 | kfree_skb(ack_skb); | ||
1075 | error_msg_to_dev: | ||
1076 | kfree(cmd); | ||
1077 | error_alloc: | ||
1078 | return result; | ||
1079 | |||
1080 | } | ||
1081 | |||
1082 | |||
1083 | /* | ||
1084 | * Query the device for its state, update the WiMAX stack's idea of it | ||
1085 | * | ||
1086 | * @i2400m: device descriptor | ||
1087 | * | ||
1088 | * Returns: 0 if ok, < 0 errno code on error. | ||
1089 | * | ||
1090 | * Executes a 'Get State' command and parses the returned | ||
1091 | * TLVs. | ||
1092 | * | ||
1093 | * Because this is almost identical to a 'Report State', we use | ||
1094 | * i2400m_report_state_hook() to parse the answer. This will set the | ||
1095 | * carrier state, as well as the RF Kill switches state. | ||
1096 | */ | ||
1097 | int i2400m_cmd_get_state(struct i2400m *i2400m) | ||
1098 | { | ||
1099 | int result; | ||
1100 | struct device *dev = i2400m_dev(i2400m); | ||
1101 | struct sk_buff *ack_skb; | ||
1102 | struct i2400m_l3l4_hdr *cmd; | ||
1103 | const struct i2400m_l3l4_hdr *ack; | ||
1104 | size_t ack_len; | ||
1105 | char strerr[32]; | ||
1106 | |||
1107 | result = -ENOMEM; | ||
1108 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
1109 | if (cmd == NULL) | ||
1110 | goto error_alloc; | ||
1111 | cmd->type = cpu_to_le16(I2400M_MT_GET_STATE); | ||
1112 | cmd->length = 0; | ||
1113 | cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); | ||
1114 | |||
1115 | ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); | ||
1116 | if (IS_ERR(ack_skb)) { | ||
1117 | dev_err(dev, "Failed to issue 'get state' command: %ld\n", | ||
1118 | PTR_ERR(ack_skb)); | ||
1119 | result = PTR_ERR(ack_skb); | ||
1120 | goto error_msg_to_dev; | ||
1121 | } | ||
1122 | ack = wimax_msg_data_len(ack_skb, &ack_len); | ||
1123 | result = i2400m_msg_check_status(ack, strerr, sizeof(strerr)); | ||
1124 | if (result < 0) { | ||
1125 | dev_err(dev, "'get state' (0x%04x) command failed: " | ||
1126 | "%d - %s\n", I2400M_MT_GET_STATE, result, strerr); | ||
1127 | goto error_cmd_failed; | ||
1128 | } | ||
1129 | i2400m_report_state_hook(i2400m, ack, ack_len - sizeof(*ack), | ||
1130 | "GET STATE"); | ||
1131 | result = 0; | ||
1132 | kfree_skb(ack_skb); | ||
1133 | error_cmd_failed: | ||
1134 | error_msg_to_dev: | ||
1135 | kfree(cmd); | ||
1136 | error_alloc: | ||
1137 | return result; | ||
1138 | } | ||
1139 | EXPORT_SYMBOL_GPL(i2400m_cmd_get_state); | ||
1140 | |||
1141 | |||
1142 | /** | ||
1143 | * Set basic configuration settings | ||
1144 | * | ||
1145 | * @i2400m: device descriptor | ||
1146 | * @args: array of pointers to the TLV headers to send for | ||
1147 | * configuration (each followed by its payload). | ||
1148 | * TLV headers and payloads must be properly initialized, with the | ||
1149 | * right endianess (LE). | ||
1150 | * @arg_size: number of pointers in the @args array | ||
1151 | */ | ||
1152 | int i2400m_set_init_config(struct i2400m *i2400m, | ||
1153 | const struct i2400m_tlv_hdr **arg, size_t args) | ||
1154 | { | ||
1155 | int result; | ||
1156 | struct device *dev = i2400m_dev(i2400m); | ||
1157 | struct sk_buff *ack_skb; | ||
1158 | struct i2400m_l3l4_hdr *cmd; | ||
1159 | char strerr[32]; | ||
1160 | unsigned argc, argsize, tlv_size; | ||
1161 | const struct i2400m_tlv_hdr *tlv_hdr; | ||
1162 | void *buf, *itr; | ||
1163 | |||
1164 | d_fnstart(3, dev, "(i2400m %p arg %p args %zu)\n", i2400m, arg, args); | ||
1165 | result = 0; | ||
1166 | if (args == 0) | ||
1167 | goto none; | ||
1168 | /* Compute the size of all the TLVs, so we can alloc a | ||
1169 | * contiguous command block to copy them. */ | ||
1170 | argsize = 0; | ||
1171 | for (argc = 0; argc < args; argc++) { | ||
1172 | tlv_hdr = arg[argc]; | ||
1173 | argsize += sizeof(*tlv_hdr) + le16_to_cpu(tlv_hdr->length); | ||
1174 | } | ||
1175 | WARN_ON(argc >= 9); /* As per hw spec */ | ||
1176 | |||
1177 | /* Alloc the space for the command and TLVs*/ | ||
1178 | result = -ENOMEM; | ||
1179 | buf = kzalloc(sizeof(*cmd) + argsize, GFP_KERNEL); | ||
1180 | if (buf == NULL) | ||
1181 | goto error_alloc; | ||
1182 | cmd = buf; | ||
1183 | cmd->type = cpu_to_le16(I2400M_MT_SET_INIT_CONFIG); | ||
1184 | cmd->length = cpu_to_le16(argsize); | ||
1185 | cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); | ||
1186 | |||
1187 | /* Copy the TLVs */ | ||
1188 | itr = buf + sizeof(*cmd); | ||
1189 | for (argc = 0; argc < args; argc++) { | ||
1190 | tlv_hdr = arg[argc]; | ||
1191 | tlv_size = sizeof(*tlv_hdr) + le16_to_cpu(tlv_hdr->length); | ||
1192 | memcpy(itr, tlv_hdr, tlv_size); | ||
1193 | itr += tlv_size; | ||
1194 | } | ||
1195 | |||
1196 | /* Send the message! */ | ||
1197 | ack_skb = i2400m_msg_to_dev(i2400m, buf, sizeof(*cmd) + argsize); | ||
1198 | result = PTR_ERR(ack_skb); | ||
1199 | if (IS_ERR(ack_skb)) { | ||
1200 | dev_err(dev, "Failed to issue 'init config' command: %d\n", | ||
1201 | result); | ||
1202 | |||
1203 | goto error_msg_to_dev; | ||
1204 | } | ||
1205 | result = i2400m_msg_check_status(wimax_msg_data(ack_skb), | ||
1206 | strerr, sizeof(strerr)); | ||
1207 | if (result < 0) | ||
1208 | dev_err(dev, "'init config' (0x%04x) command failed: %d - %s\n", | ||
1209 | I2400M_MT_SET_INIT_CONFIG, result, strerr); | ||
1210 | kfree_skb(ack_skb); | ||
1211 | error_msg_to_dev: | ||
1212 | kfree(buf); | ||
1213 | error_alloc: | ||
1214 | none: | ||
1215 | d_fnend(3, dev, "(i2400m %p arg %p args %zu) = %d\n", | ||
1216 | i2400m, arg, args, result); | ||
1217 | return result; | ||
1218 | |||
1219 | } | ||
1220 | EXPORT_SYMBOL_GPL(i2400m_set_init_config); | ||
1221 | |||
1222 | |||
1223 | /** | ||
1224 | * i2400m_dev_initialize - Initialize the device once communications are ready | ||
1225 | * | ||
1226 | * @i2400m: device descriptor | ||
1227 | * | ||
1228 | * Returns: 0 if ok, < 0 errno code on error. | ||
1229 | * | ||
1230 | * Configures the device to work the way we like it. | ||
1231 | * | ||
1232 | * At the point of this call, the device is registered with the WiMAX | ||
1233 | * and netdev stacks, firmware is uploaded and we can talk to the | ||
1234 | * device normally. | ||
1235 | */ | ||
1236 | int i2400m_dev_initialize(struct i2400m *i2400m) | ||
1237 | { | ||
1238 | int result; | ||
1239 | struct device *dev = i2400m_dev(i2400m); | ||
1240 | struct i2400m_tlv_config_idle_parameters idle_params; | ||
1241 | const struct i2400m_tlv_hdr *args[9]; | ||
1242 | unsigned argc = 0; | ||
1243 | |||
1244 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
1245 | /* Useless for now...might change */ | ||
1246 | if (i2400m_idle_mode_disabled) { | ||
1247 | idle_params.hdr.type = | ||
1248 | cpu_to_le16(I2400M_TLV_CONFIG_IDLE_PARAMETERS); | ||
1249 | idle_params.hdr.length = cpu_to_le16( | ||
1250 | sizeof(idle_params) - sizeof(idle_params.hdr)); | ||
1251 | idle_params.idle_timeout = 0; | ||
1252 | idle_params.idle_paging_interval = 0; | ||
1253 | args[argc++] = &idle_params.hdr; | ||
1254 | } | ||
1255 | result = i2400m_set_init_config(i2400m, args, argc); | ||
1256 | if (result < 0) | ||
1257 | goto error; | ||
1258 | result = i2400m_firmware_check(i2400m); /* fw versions ok? */ | ||
1259 | if (result < 0) | ||
1260 | goto error; | ||
1261 | /* | ||
1262 | * Update state: Here it just calls a get state; parsing the | ||
1263 | * result (System State TLV and RF Status TLV [done in the rx | ||
1264 | * path hooks]) will set the hardware and software RF-Kill | ||
1265 | * status. | ||
1266 | */ | ||
1267 | result = i2400m_cmd_get_state(i2400m); | ||
1268 | error: | ||
1269 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
1270 | return result; | ||
1271 | } | ||
1272 | |||
1273 | |||
1274 | /** | ||
1275 | * i2400m_dev_shutdown - Shutdown a running device | ||
1276 | * | ||
1277 | * @i2400m: device descriptor | ||
1278 | * | ||
1279 | * Gracefully stops the device, moving it to the lowest power | ||
1280 | * consumption state possible. | ||
1281 | */ | ||
1282 | void i2400m_dev_shutdown(struct i2400m *i2400m) | ||
1283 | { | ||
1284 | int result = -ENODEV; | ||
1285 | struct device *dev = i2400m_dev(i2400m); | ||
1286 | |||
1287 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
1288 | result = i2400m->bus_reset(i2400m, I2400M_RT_WARM); | ||
1289 | d_fnend(3, dev, "(i2400m %p) = void [%d]\n", i2400m, result); | ||
1290 | return; | ||
1291 | } | ||
diff --git a/drivers/net/wimax/i2400m/debug-levels.h b/drivers/net/wimax/i2400m/debug-levels.h new file mode 100644 index 000000000000..3183baa16a52 --- /dev/null +++ b/drivers/net/wimax/i2400m/debug-levels.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Debug levels control file for the i2400m module | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | */ | ||
23 | #ifndef __debug_levels__h__ | ||
24 | #define __debug_levels__h__ | ||
25 | |||
26 | /* Maximum compile and run time debug level for all submodules */ | ||
27 | #define D_MODULENAME i2400m | ||
28 | #define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL | ||
29 | |||
30 | #include <linux/wimax/debug.h> | ||
31 | |||
32 | /* List of all the enabled modules */ | ||
33 | enum d_module { | ||
34 | D_SUBMODULE_DECLARE(control), | ||
35 | D_SUBMODULE_DECLARE(driver), | ||
36 | D_SUBMODULE_DECLARE(debugfs), | ||
37 | D_SUBMODULE_DECLARE(fw), | ||
38 | D_SUBMODULE_DECLARE(netdev), | ||
39 | D_SUBMODULE_DECLARE(rfkill), | ||
40 | D_SUBMODULE_DECLARE(rx), | ||
41 | D_SUBMODULE_DECLARE(tx), | ||
42 | }; | ||
43 | |||
44 | |||
45 | #endif /* #ifndef __debug_levels__h__ */ | ||
diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c new file mode 100644 index 000000000000..626632985977 --- /dev/null +++ b/drivers/net/wimax/i2400m/debugfs.c | |||
@@ -0,0 +1,392 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Debugfs interfaces to manipulate driver and device information | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/debugfs.h> | ||
25 | #include <linux/netdevice.h> | ||
26 | #include <linux/etherdevice.h> | ||
27 | #include <linux/spinlock.h> | ||
28 | #include <linux/device.h> | ||
29 | #include "i2400m.h" | ||
30 | |||
31 | |||
32 | #define D_SUBMODULE debugfs | ||
33 | #include "debug-levels.h" | ||
34 | |||
35 | static | ||
36 | int debugfs_netdev_queue_stopped_get(void *data, u64 *val) | ||
37 | { | ||
38 | struct i2400m *i2400m = data; | ||
39 | *val = netif_queue_stopped(i2400m->wimax_dev.net_dev); | ||
40 | return 0; | ||
41 | } | ||
42 | DEFINE_SIMPLE_ATTRIBUTE(fops_netdev_queue_stopped, | ||
43 | debugfs_netdev_queue_stopped_get, | ||
44 | NULL, "%llu\n"); | ||
45 | |||
46 | |||
47 | static | ||
48 | struct dentry *debugfs_create_netdev_queue_stopped( | ||
49 | const char *name, struct dentry *parent, struct i2400m *i2400m) | ||
50 | { | ||
51 | return debugfs_create_file(name, 0400, parent, i2400m, | ||
52 | &fops_netdev_queue_stopped); | ||
53 | } | ||
54 | |||
55 | |||
56 | /* | ||
57 | * inode->i_private has the @data argument to debugfs_create_file() | ||
58 | */ | ||
59 | static | ||
60 | int i2400m_stats_open(struct inode *inode, struct file *filp) | ||
61 | { | ||
62 | filp->private_data = inode->i_private; | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * We don't allow partial reads of this file, as then the reader would | ||
68 | * get weirdly confused data as it is updated. | ||
69 | * | ||
70 | * So or you read it all or nothing; if you try to read with an offset | ||
71 | * != 0, we consider you are done reading. | ||
72 | */ | ||
73 | static | ||
74 | ssize_t i2400m_rx_stats_read(struct file *filp, char __user *buffer, | ||
75 | size_t count, loff_t *ppos) | ||
76 | { | ||
77 | struct i2400m *i2400m = filp->private_data; | ||
78 | char buf[128]; | ||
79 | unsigned long flags; | ||
80 | |||
81 | if (*ppos != 0) | ||
82 | return 0; | ||
83 | if (count < sizeof(buf)) | ||
84 | return -ENOSPC; | ||
85 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
86 | snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", | ||
87 | i2400m->rx_pl_num, i2400m->rx_pl_min, | ||
88 | i2400m->rx_pl_max, i2400m->rx_num, | ||
89 | i2400m->rx_size_acc, | ||
90 | i2400m->rx_size_min, i2400m->rx_size_max); | ||
91 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
92 | return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); | ||
93 | } | ||
94 | |||
95 | |||
96 | /* Any write clears the stats */ | ||
97 | static | ||
98 | ssize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer, | ||
99 | size_t count, loff_t *ppos) | ||
100 | { | ||
101 | struct i2400m *i2400m = filp->private_data; | ||
102 | unsigned long flags; | ||
103 | |||
104 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
105 | i2400m->rx_pl_num = 0; | ||
106 | i2400m->rx_pl_max = 0; | ||
107 | i2400m->rx_pl_min = UINT_MAX; | ||
108 | i2400m->rx_num = 0; | ||
109 | i2400m->rx_size_acc = 0; | ||
110 | i2400m->rx_size_min = UINT_MAX; | ||
111 | i2400m->rx_size_max = 0; | ||
112 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
113 | return count; | ||
114 | } | ||
115 | |||
116 | static | ||
117 | const struct file_operations i2400m_rx_stats_fops = { | ||
118 | .owner = THIS_MODULE, | ||
119 | .open = i2400m_stats_open, | ||
120 | .read = i2400m_rx_stats_read, | ||
121 | .write = i2400m_rx_stats_write, | ||
122 | }; | ||
123 | |||
124 | |||
125 | /* See i2400m_rx_stats_read() */ | ||
126 | static | ||
127 | ssize_t i2400m_tx_stats_read(struct file *filp, char __user *buffer, | ||
128 | size_t count, loff_t *ppos) | ||
129 | { | ||
130 | struct i2400m *i2400m = filp->private_data; | ||
131 | char buf[128]; | ||
132 | unsigned long flags; | ||
133 | |||
134 | if (*ppos != 0) | ||
135 | return 0; | ||
136 | if (count < sizeof(buf)) | ||
137 | return -ENOSPC; | ||
138 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
139 | snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", | ||
140 | i2400m->tx_pl_num, i2400m->tx_pl_min, | ||
141 | i2400m->tx_pl_max, i2400m->tx_num, | ||
142 | i2400m->tx_size_acc, | ||
143 | i2400m->tx_size_min, i2400m->tx_size_max); | ||
144 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
145 | return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); | ||
146 | } | ||
147 | |||
148 | /* Any write clears the stats */ | ||
149 | static | ||
150 | ssize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer, | ||
151 | size_t count, loff_t *ppos) | ||
152 | { | ||
153 | struct i2400m *i2400m = filp->private_data; | ||
154 | unsigned long flags; | ||
155 | |||
156 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
157 | i2400m->tx_pl_num = 0; | ||
158 | i2400m->tx_pl_max = 0; | ||
159 | i2400m->tx_pl_min = UINT_MAX; | ||
160 | i2400m->tx_num = 0; | ||
161 | i2400m->tx_size_acc = 0; | ||
162 | i2400m->tx_size_min = UINT_MAX; | ||
163 | i2400m->tx_size_max = 0; | ||
164 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
165 | return count; | ||
166 | } | ||
167 | |||
168 | static | ||
169 | const struct file_operations i2400m_tx_stats_fops = { | ||
170 | .owner = THIS_MODULE, | ||
171 | .open = i2400m_stats_open, | ||
172 | .read = i2400m_tx_stats_read, | ||
173 | .write = i2400m_tx_stats_write, | ||
174 | }; | ||
175 | |||
176 | |||
177 | /* Write 1 to ask the device to go into suspend */ | ||
178 | static | ||
179 | int debugfs_i2400m_suspend_set(void *data, u64 val) | ||
180 | { | ||
181 | int result; | ||
182 | struct i2400m *i2400m = data; | ||
183 | result = i2400m_cmd_enter_powersave(i2400m); | ||
184 | if (result >= 0) | ||
185 | result = 0; | ||
186 | return result; | ||
187 | } | ||
188 | DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_suspend, | ||
189 | NULL, debugfs_i2400m_suspend_set, | ||
190 | "%llu\n"); | ||
191 | |||
192 | static | ||
193 | struct dentry *debugfs_create_i2400m_suspend( | ||
194 | const char *name, struct dentry *parent, struct i2400m *i2400m) | ||
195 | { | ||
196 | return debugfs_create_file(name, 0200, parent, i2400m, | ||
197 | &fops_i2400m_suspend); | ||
198 | } | ||
199 | |||
200 | |||
201 | /* | ||
202 | * Reset the device | ||
203 | * | ||
204 | * Write 0 to ask the device to soft reset, 1 to cold reset, 2 to bus | ||
205 | * reset (as defined by enum i2400m_reset_type). | ||
206 | */ | ||
207 | static | ||
208 | int debugfs_i2400m_reset_set(void *data, u64 val) | ||
209 | { | ||
210 | int result; | ||
211 | struct i2400m *i2400m = data; | ||
212 | enum i2400m_reset_type rt = val; | ||
213 | switch(rt) { | ||
214 | case I2400M_RT_WARM: | ||
215 | case I2400M_RT_COLD: | ||
216 | case I2400M_RT_BUS: | ||
217 | result = i2400m->bus_reset(i2400m, rt); | ||
218 | if (result >= 0) | ||
219 | result = 0; | ||
220 | default: | ||
221 | result = -EINVAL; | ||
222 | } | ||
223 | return result; | ||
224 | } | ||
225 | DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_reset, | ||
226 | NULL, debugfs_i2400m_reset_set, | ||
227 | "%llu\n"); | ||
228 | |||
229 | static | ||
230 | struct dentry *debugfs_create_i2400m_reset( | ||
231 | const char *name, struct dentry *parent, struct i2400m *i2400m) | ||
232 | { | ||
233 | return debugfs_create_file(name, 0200, parent, i2400m, | ||
234 | &fops_i2400m_reset); | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * Debug levels control; see debug.h | ||
239 | */ | ||
240 | struct d_level D_LEVEL[] = { | ||
241 | D_SUBMODULE_DEFINE(control), | ||
242 | D_SUBMODULE_DEFINE(driver), | ||
243 | D_SUBMODULE_DEFINE(debugfs), | ||
244 | D_SUBMODULE_DEFINE(fw), | ||
245 | D_SUBMODULE_DEFINE(netdev), | ||
246 | D_SUBMODULE_DEFINE(rfkill), | ||
247 | D_SUBMODULE_DEFINE(rx), | ||
248 | D_SUBMODULE_DEFINE(tx), | ||
249 | }; | ||
250 | size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); | ||
251 | |||
252 | #define __debugfs_register(prefix, name, parent) \ | ||
253 | do { \ | ||
254 | result = d_level_register_debugfs(prefix, name, parent); \ | ||
255 | if (result < 0) \ | ||
256 | goto error; \ | ||
257 | } while (0) | ||
258 | |||
259 | |||
260 | int i2400m_debugfs_add(struct i2400m *i2400m) | ||
261 | { | ||
262 | int result; | ||
263 | struct device *dev = i2400m_dev(i2400m); | ||
264 | struct dentry *dentry = i2400m->wimax_dev.debugfs_dentry; | ||
265 | struct dentry *fd; | ||
266 | |||
267 | dentry = debugfs_create_dir("i2400m", dentry); | ||
268 | result = PTR_ERR(dentry); | ||
269 | if (IS_ERR(dentry)) { | ||
270 | if (result == -ENODEV) | ||
271 | result = 0; /* No debugfs support */ | ||
272 | goto error; | ||
273 | } | ||
274 | i2400m->debugfs_dentry = dentry; | ||
275 | __debugfs_register("dl_", control, dentry); | ||
276 | __debugfs_register("dl_", driver, dentry); | ||
277 | __debugfs_register("dl_", debugfs, dentry); | ||
278 | __debugfs_register("dl_", fw, dentry); | ||
279 | __debugfs_register("dl_", netdev, dentry); | ||
280 | __debugfs_register("dl_", rfkill, dentry); | ||
281 | __debugfs_register("dl_", rx, dentry); | ||
282 | __debugfs_register("dl_", tx, dentry); | ||
283 | |||
284 | fd = debugfs_create_size_t("tx_in", 0400, dentry, | ||
285 | &i2400m->tx_in); | ||
286 | result = PTR_ERR(fd); | ||
287 | if (IS_ERR(fd) && result != -ENODEV) { | ||
288 | dev_err(dev, "Can't create debugfs entry " | ||
289 | "tx_in: %d\n", result); | ||
290 | goto error; | ||
291 | } | ||
292 | |||
293 | fd = debugfs_create_size_t("tx_out", 0400, dentry, | ||
294 | &i2400m->tx_out); | ||
295 | result = PTR_ERR(fd); | ||
296 | if (IS_ERR(fd) && result != -ENODEV) { | ||
297 | dev_err(dev, "Can't create debugfs entry " | ||
298 | "tx_out: %d\n", result); | ||
299 | goto error; | ||
300 | } | ||
301 | |||
302 | fd = debugfs_create_u32("state", 0600, dentry, | ||
303 | &i2400m->state); | ||
304 | result = PTR_ERR(fd); | ||
305 | if (IS_ERR(fd) && result != -ENODEV) { | ||
306 | dev_err(dev, "Can't create debugfs entry " | ||
307 | "state: %d\n", result); | ||
308 | goto error; | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * Trace received messages from user space | ||
313 | * | ||
314 | * In order to tap the bidirectional message stream in the | ||
315 | * 'msg' pipe, user space can read from the 'msg' pipe; | ||
316 | * however, due to limitations in libnl, we can't know what | ||
317 | * the different applications are sending down to the kernel. | ||
318 | * | ||
319 | * So we have this hack where the driver will echo any message | ||
320 | * received on the msg pipe from user space [through a call to | ||
321 | * wimax_dev->op_msg_from_user() into | ||
322 | * i2400m_op_msg_from_user()] into the 'trace' pipe that this | ||
323 | * driver creates. | ||
324 | * | ||
325 | * So then, reading from both the 'trace' and 'msg' pipes in | ||
326 | * user space will provide a full dump of the traffic. | ||
327 | * | ||
328 | * Write 1 to activate, 0 to clear. | ||
329 | * | ||
330 | * It is not really very atomic, but it is also not too | ||
331 | * critical. | ||
332 | */ | ||
333 | fd = debugfs_create_u8("trace_msg_from_user", 0600, dentry, | ||
334 | &i2400m->trace_msg_from_user); | ||
335 | result = PTR_ERR(fd); | ||
336 | if (IS_ERR(fd) && result != -ENODEV) { | ||
337 | dev_err(dev, "Can't create debugfs entry " | ||
338 | "trace_msg_from_user: %d\n", result); | ||
339 | goto error; | ||
340 | } | ||
341 | |||
342 | fd = debugfs_create_netdev_queue_stopped("netdev_queue_stopped", | ||
343 | dentry, i2400m); | ||
344 | result = PTR_ERR(fd); | ||
345 | if (IS_ERR(fd) && result != -ENODEV) { | ||
346 | dev_err(dev, "Can't create debugfs entry " | ||
347 | "netdev_queue_stopped: %d\n", result); | ||
348 | goto error; | ||
349 | } | ||
350 | |||
351 | fd = debugfs_create_file("rx_stats", 0600, dentry, i2400m, | ||
352 | &i2400m_rx_stats_fops); | ||
353 | result = PTR_ERR(fd); | ||
354 | if (IS_ERR(fd) && result != -ENODEV) { | ||
355 | dev_err(dev, "Can't create debugfs entry " | ||
356 | "rx_stats: %d\n", result); | ||
357 | goto error; | ||
358 | } | ||
359 | |||
360 | fd = debugfs_create_file("tx_stats", 0600, dentry, i2400m, | ||
361 | &i2400m_tx_stats_fops); | ||
362 | result = PTR_ERR(fd); | ||
363 | if (IS_ERR(fd) && result != -ENODEV) { | ||
364 | dev_err(dev, "Can't create debugfs entry " | ||
365 | "tx_stats: %d\n", result); | ||
366 | goto error; | ||
367 | } | ||
368 | |||
369 | fd = debugfs_create_i2400m_suspend("suspend", dentry, i2400m); | ||
370 | result = PTR_ERR(fd); | ||
371 | if (IS_ERR(fd) && result != -ENODEV) { | ||
372 | dev_err(dev, "Can't create debugfs entry suspend: %d\n", | ||
373 | result); | ||
374 | goto error; | ||
375 | } | ||
376 | |||
377 | fd = debugfs_create_i2400m_reset("reset", dentry, i2400m); | ||
378 | result = PTR_ERR(fd); | ||
379 | if (IS_ERR(fd) && result != -ENODEV) { | ||
380 | dev_err(dev, "Can't create debugfs entry reset: %d\n", result); | ||
381 | goto error; | ||
382 | } | ||
383 | |||
384 | result = 0; | ||
385 | error: | ||
386 | return result; | ||
387 | } | ||
388 | |||
389 | void i2400m_debugfs_rm(struct i2400m *i2400m) | ||
390 | { | ||
391 | debugfs_remove_recursive(i2400m->debugfs_dentry); | ||
392 | } | ||
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c new file mode 100644 index 000000000000..5f98047e18cf --- /dev/null +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -0,0 +1,728 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Generic probe/disconnect, reset and message passing | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | * | ||
23 | * | ||
24 | * See i2400m.h for driver documentation. This contains helpers for | ||
25 | * the driver model glue [_setup()/_release()], handling device resets | ||
26 | * [_dev_reset_handle()], and the backends for the WiMAX stack ops | ||
27 | * reset [_op_reset()] and message from user [_op_msg_from_user()]. | ||
28 | * | ||
29 | * ROADMAP: | ||
30 | * | ||
31 | * i2400m_op_msg_from_user() | ||
32 | * i2400m_msg_to_dev() | ||
33 | * wimax_msg_to_user_send() | ||
34 | * | ||
35 | * i2400m_op_reset() | ||
36 | * i240m->bus_reset() | ||
37 | * | ||
38 | * i2400m_dev_reset_handle() | ||
39 | * __i2400m_dev_reset_handle() | ||
40 | * __i2400m_dev_stop() | ||
41 | * __i2400m_dev_start() | ||
42 | * | ||
43 | * i2400m_setup() | ||
44 | * i2400m_bootrom_init() | ||
45 | * register_netdev() | ||
46 | * i2400m_dev_start() | ||
47 | * __i2400m_dev_start() | ||
48 | * i2400m_dev_bootstrap() | ||
49 | * i2400m_tx_setup() | ||
50 | * i2400m->bus_dev_start() | ||
51 | * i2400m_check_mac_addr() | ||
52 | * wimax_dev_add() | ||
53 | * | ||
54 | * i2400m_release() | ||
55 | * wimax_dev_rm() | ||
56 | * i2400m_dev_stop() | ||
57 | * __i2400m_dev_stop() | ||
58 | * i2400m_dev_shutdown() | ||
59 | * i2400m->bus_dev_stop() | ||
60 | * i2400m_tx_release() | ||
61 | * unregister_netdev() | ||
62 | */ | ||
63 | #include "i2400m.h" | ||
64 | #include <linux/wimax/i2400m.h> | ||
65 | #include <linux/module.h> | ||
66 | #include <linux/moduleparam.h> | ||
67 | |||
68 | #define D_SUBMODULE driver | ||
69 | #include "debug-levels.h" | ||
70 | |||
71 | |||
72 | int i2400m_idle_mode_disabled; /* 0 (idle mode enabled) by default */ | ||
73 | module_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644); | ||
74 | MODULE_PARM_DESC(idle_mode_disabled, | ||
75 | "If true, the device will not enable idle mode negotiation " | ||
76 | "with the base station (when connected) to save power."); | ||
77 | |||
78 | /** | ||
79 | * i2400m_queue_work - schedule work on a i2400m's queue | ||
80 | * | ||
81 | * @i2400m: device descriptor | ||
82 | * | ||
83 | * @fn: function to run to execute work. It gets passed a 'struct | ||
84 | * work_struct' that is wrapped in a 'struct i2400m_work'. Once | ||
85 | * done, you have to (1) i2400m_put(i2400m_work->i2400m) and then | ||
86 | * (2) kfree(i2400m_work). | ||
87 | * | ||
88 | * @gfp_flags: GFP flags for memory allocation. | ||
89 | * | ||
90 | * @pl: pointer to a payload buffer that you want to pass to the _work | ||
91 | * function. Use this to pack (for example) a struct with extra | ||
92 | * arguments. | ||
93 | * | ||
94 | * @pl_size: size of the payload buffer. | ||
95 | * | ||
96 | * We do this quite often, so this just saves typing; allocate a | ||
97 | * wrapper for a i2400m, get a ref to it, pack arguments and launch | ||
98 | * the work. | ||
99 | * | ||
100 | * A usual workflow is: | ||
101 | * | ||
102 | * struct my_work_args { | ||
103 | * void *something; | ||
104 | * int whatever; | ||
105 | * }; | ||
106 | * ... | ||
107 | * | ||
108 | * struct my_work_args my_args = { | ||
109 | * .something = FOO, | ||
110 | * .whaetever = BLAH | ||
111 | * }; | ||
112 | * i2400m_queue_work(i2400m, 1, my_work_function, GFP_KERNEL, | ||
113 | * &args, sizeof(args)) | ||
114 | * | ||
115 | * And now the work function can unpack the arguments and call the | ||
116 | * real function (or do the job itself): | ||
117 | * | ||
118 | * static | ||
119 | * void my_work_fn((struct work_struct *ws) | ||
120 | * { | ||
121 | * struct i2400m_work *iw = | ||
122 | * container_of(ws, struct i2400m_work, ws); | ||
123 | * struct my_work_args *my_args = (void *) iw->pl; | ||
124 | * | ||
125 | * my_work(iw->i2400m, my_args->something, my_args->whatevert); | ||
126 | * } | ||
127 | */ | ||
128 | int i2400m_queue_work(struct i2400m *i2400m, | ||
129 | void (*fn)(struct work_struct *), gfp_t gfp_flags, | ||
130 | const void *pl, size_t pl_size) | ||
131 | { | ||
132 | int result; | ||
133 | struct i2400m_work *iw; | ||
134 | |||
135 | BUG_ON(i2400m->work_queue == NULL); | ||
136 | result = -ENOMEM; | ||
137 | iw = kzalloc(sizeof(*iw) + pl_size, gfp_flags); | ||
138 | if (iw == NULL) | ||
139 | goto error_kzalloc; | ||
140 | iw->i2400m = i2400m_get(i2400m); | ||
141 | memcpy(iw->pl, pl, pl_size); | ||
142 | INIT_WORK(&iw->ws, fn); | ||
143 | result = queue_work(i2400m->work_queue, &iw->ws); | ||
144 | error_kzalloc: | ||
145 | return result; | ||
146 | } | ||
147 | EXPORT_SYMBOL_GPL(i2400m_queue_work); | ||
148 | |||
149 | |||
150 | /* | ||
151 | * Schedule i2400m's specific work on the system's queue. | ||
152 | * | ||
153 | * Used for a few cases where we really need it; otherwise, identical | ||
154 | * to i2400m_queue_work(). | ||
155 | * | ||
156 | * Returns < 0 errno code on error, 1 if ok. | ||
157 | * | ||
158 | * If it returns zero, something really bad happened, as it means the | ||
159 | * works struct was already queued, but we have just allocated it, so | ||
160 | * it should not happen. | ||
161 | */ | ||
162 | int i2400m_schedule_work(struct i2400m *i2400m, | ||
163 | void (*fn)(struct work_struct *), gfp_t gfp_flags) | ||
164 | { | ||
165 | int result; | ||
166 | struct i2400m_work *iw; | ||
167 | |||
168 | BUG_ON(i2400m->work_queue == NULL); | ||
169 | result = -ENOMEM; | ||
170 | iw = kzalloc(sizeof(*iw), gfp_flags); | ||
171 | if (iw == NULL) | ||
172 | goto error_kzalloc; | ||
173 | iw->i2400m = i2400m_get(i2400m); | ||
174 | INIT_WORK(&iw->ws, fn); | ||
175 | result = schedule_work(&iw->ws); | ||
176 | if (result == 0) | ||
177 | result = -ENXIO; | ||
178 | error_kzalloc: | ||
179 | return result; | ||
180 | } | ||
181 | |||
182 | |||
183 | /* | ||
184 | * WiMAX stack operation: relay a message from user space | ||
185 | * | ||
186 | * @wimax_dev: device descriptor | ||
187 | * @pipe_name: named pipe the message is for | ||
188 | * @msg_buf: pointer to the message bytes | ||
189 | * @msg_len: length of the buffer | ||
190 | * @genl_info: passed by the generic netlink layer | ||
191 | * | ||
192 | * The WiMAX stack will call this function when a message was received | ||
193 | * from user space. | ||
194 | * | ||
195 | * For the i2400m, this is an L3L4 message, as specified in | ||
196 | * include/linux/wimax/i2400m.h, and thus prefixed with a 'struct | ||
197 | * i2400m_l3l4_hdr'. Driver (and device) expect the messages to be | ||
198 | * coded in Little Endian. | ||
199 | * | ||
200 | * This function just verifies that the header declaration and the | ||
201 | * payload are consistent and then deals with it, either forwarding it | ||
202 | * to the device or procesing it locally. | ||
203 | * | ||
204 | * In the i2400m, messages are basically commands that will carry an | ||
205 | * ack, so we use i2400m_msg_to_dev() and then deliver the ack back to | ||
206 | * user space. The rx.c code might intercept the response and use it | ||
207 | * to update the driver's state, but then it will pass it on so it can | ||
208 | * be relayed back to user space. | ||
209 | * | ||
210 | * Note that asynchronous events from the device are processed and | ||
211 | * sent to user space in rx.c. | ||
212 | */ | ||
213 | static | ||
214 | int i2400m_op_msg_from_user(struct wimax_dev *wimax_dev, | ||
215 | const char *pipe_name, | ||
216 | const void *msg_buf, size_t msg_len, | ||
217 | const struct genl_info *genl_info) | ||
218 | { | ||
219 | int result; | ||
220 | struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); | ||
221 | struct device *dev = i2400m_dev(i2400m); | ||
222 | struct sk_buff *ack_skb; | ||
223 | |||
224 | d_fnstart(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p " | ||
225 | "msg_len %zu genl_info %p)\n", wimax_dev, i2400m, | ||
226 | msg_buf, msg_len, genl_info); | ||
227 | ack_skb = i2400m_msg_to_dev(i2400m, msg_buf, msg_len); | ||
228 | result = PTR_ERR(ack_skb); | ||
229 | if (IS_ERR(ack_skb)) | ||
230 | goto error_msg_to_dev; | ||
231 | if (unlikely(i2400m->trace_msg_from_user)) | ||
232 | wimax_msg(&i2400m->wimax_dev, "trace", | ||
233 | msg_buf, msg_len, GFP_KERNEL); | ||
234 | result = wimax_msg_send(&i2400m->wimax_dev, ack_skb); | ||
235 | error_msg_to_dev: | ||
236 | d_fnend(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p msg_len %zu " | ||
237 | "genl_info %p) = %d\n", wimax_dev, i2400m, msg_buf, msg_len, | ||
238 | genl_info, result); | ||
239 | return result; | ||
240 | } | ||
241 | |||
242 | |||
243 | /* | ||
244 | * Context to wait for a reset to finalize | ||
245 | */ | ||
246 | struct i2400m_reset_ctx { | ||
247 | struct completion completion; | ||
248 | int result; | ||
249 | }; | ||
250 | |||
251 | |||
252 | /* | ||
253 | * WiMAX stack operation: reset a device | ||
254 | * | ||
255 | * @wimax_dev: device descriptor | ||
256 | * | ||
257 | * See the documentation for wimax_reset() and wimax_dev->op_reset for | ||
258 | * the requirements of this function. The WiMAX stack guarantees | ||
259 | * serialization on calls to this function. | ||
260 | * | ||
261 | * Do a warm reset on the device; if it fails, resort to a cold reset | ||
262 | * and return -ENODEV. On successful warm reset, we need to block | ||
263 | * until it is complete. | ||
264 | * | ||
265 | * The bus-driver implementation of reset takes care of falling back | ||
266 | * to cold reset if warm fails. | ||
267 | */ | ||
268 | static | ||
269 | int i2400m_op_reset(struct wimax_dev *wimax_dev) | ||
270 | { | ||
271 | int result; | ||
272 | struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); | ||
273 | struct device *dev = i2400m_dev(i2400m); | ||
274 | struct i2400m_reset_ctx ctx = { | ||
275 | .completion = COMPLETION_INITIALIZER_ONSTACK(ctx.completion), | ||
276 | .result = 0, | ||
277 | }; | ||
278 | |||
279 | d_fnstart(4, dev, "(wimax_dev %p)\n", wimax_dev); | ||
280 | mutex_lock(&i2400m->init_mutex); | ||
281 | i2400m->reset_ctx = &ctx; | ||
282 | mutex_unlock(&i2400m->init_mutex); | ||
283 | result = i2400m->bus_reset(i2400m, I2400M_RT_WARM); | ||
284 | if (result < 0) | ||
285 | goto out; | ||
286 | result = wait_for_completion_timeout(&ctx.completion, 4*HZ); | ||
287 | if (result == 0) | ||
288 | result = -ETIMEDOUT; | ||
289 | else if (result > 0) | ||
290 | result = ctx.result; | ||
291 | /* if result < 0, pass it on */ | ||
292 | mutex_lock(&i2400m->init_mutex); | ||
293 | i2400m->reset_ctx = NULL; | ||
294 | mutex_unlock(&i2400m->init_mutex); | ||
295 | out: | ||
296 | d_fnend(4, dev, "(wimax_dev %p) = %d\n", wimax_dev, result); | ||
297 | return result; | ||
298 | } | ||
299 | |||
300 | |||
301 | /* | ||
302 | * Check the MAC address we got from boot mode is ok | ||
303 | * | ||
304 | * @i2400m: device descriptor | ||
305 | * | ||
306 | * Returns: 0 if ok, < 0 errno code on error. | ||
307 | */ | ||
308 | static | ||
309 | int i2400m_check_mac_addr(struct i2400m *i2400m) | ||
310 | { | ||
311 | int result; | ||
312 | struct device *dev = i2400m_dev(i2400m); | ||
313 | struct sk_buff *skb; | ||
314 | const struct i2400m_tlv_detailed_device_info *ddi; | ||
315 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
316 | const unsigned char zeromac[ETH_ALEN] = { 0 }; | ||
317 | |||
318 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
319 | skb = i2400m_get_device_info(i2400m); | ||
320 | if (IS_ERR(skb)) { | ||
321 | result = PTR_ERR(skb); | ||
322 | dev_err(dev, "Cannot verify MAC address, error reading: %d\n", | ||
323 | result); | ||
324 | goto error; | ||
325 | } | ||
326 | /* Extract MAC addresss */ | ||
327 | ddi = (void *) skb->data; | ||
328 | BUILD_BUG_ON(ETH_ALEN != sizeof(ddi->mac_address)); | ||
329 | d_printf(2, dev, "GET DEVICE INFO: mac addr " | ||
330 | "%02x:%02x:%02x:%02x:%02x:%02x\n", | ||
331 | ddi->mac_address[0], ddi->mac_address[1], | ||
332 | ddi->mac_address[2], ddi->mac_address[3], | ||
333 | ddi->mac_address[4], ddi->mac_address[5]); | ||
334 | if (!memcmp(net_dev->perm_addr, ddi->mac_address, | ||
335 | sizeof(ddi->mac_address))) | ||
336 | goto ok; | ||
337 | dev_warn(dev, "warning: device reports a different MAC address " | ||
338 | "to that of boot mode's\n"); | ||
339 | dev_warn(dev, "device reports %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
340 | ddi->mac_address[0], ddi->mac_address[1], | ||
341 | ddi->mac_address[2], ddi->mac_address[3], | ||
342 | ddi->mac_address[4], ddi->mac_address[5]); | ||
343 | dev_warn(dev, "boot mode reported %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
344 | net_dev->perm_addr[0], net_dev->perm_addr[1], | ||
345 | net_dev->perm_addr[2], net_dev->perm_addr[3], | ||
346 | net_dev->perm_addr[4], net_dev->perm_addr[5]); | ||
347 | if (!memcmp(zeromac, ddi->mac_address, sizeof(zeromac))) | ||
348 | dev_err(dev, "device reports an invalid MAC address, " | ||
349 | "not updating\n"); | ||
350 | else { | ||
351 | dev_warn(dev, "updating MAC address\n"); | ||
352 | net_dev->addr_len = ETH_ALEN; | ||
353 | memcpy(net_dev->perm_addr, ddi->mac_address, ETH_ALEN); | ||
354 | memcpy(net_dev->dev_addr, ddi->mac_address, ETH_ALEN); | ||
355 | } | ||
356 | ok: | ||
357 | result = 0; | ||
358 | kfree_skb(skb); | ||
359 | error: | ||
360 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
361 | return result; | ||
362 | } | ||
363 | |||
364 | |||
365 | /** | ||
366 | * __i2400m_dev_start - Bring up driver communication with the device | ||
367 | * | ||
368 | * @i2400m: device descriptor | ||
369 | * @flags: boot mode flags | ||
370 | * | ||
371 | * Returns: 0 if ok, < 0 errno code on error. | ||
372 | * | ||
373 | * Uploads firmware and brings up all the resources needed to be able | ||
374 | * to communicate with the device. | ||
375 | * | ||
376 | * TX needs to be setup before the bus-specific code (otherwise on | ||
377 | * shutdown, the bus-tx code could try to access it). | ||
378 | */ | ||
379 | static | ||
380 | int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags) | ||
381 | { | ||
382 | int result; | ||
383 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
384 | struct net_device *net_dev = wimax_dev->net_dev; | ||
385 | struct device *dev = i2400m_dev(i2400m); | ||
386 | int times = 3; | ||
387 | |||
388 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
389 | retry: | ||
390 | result = i2400m_dev_bootstrap(i2400m, flags); | ||
391 | if (result < 0) { | ||
392 | dev_err(dev, "cannot bootstrap device: %d\n", result); | ||
393 | goto error_bootstrap; | ||
394 | } | ||
395 | result = i2400m_tx_setup(i2400m); | ||
396 | if (result < 0) | ||
397 | goto error_tx_setup; | ||
398 | result = i2400m->bus_dev_start(i2400m); | ||
399 | if (result < 0) | ||
400 | goto error_bus_dev_start; | ||
401 | i2400m->work_queue = create_singlethread_workqueue(wimax_dev->name); | ||
402 | if (i2400m->work_queue == NULL) { | ||
403 | result = -ENOMEM; | ||
404 | dev_err(dev, "cannot create workqueue\n"); | ||
405 | goto error_create_workqueue; | ||
406 | } | ||
407 | /* At this point is ok to send commands to the device */ | ||
408 | result = i2400m_check_mac_addr(i2400m); | ||
409 | if (result < 0) | ||
410 | goto error_check_mac_addr; | ||
411 | i2400m->ready = 1; | ||
412 | wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); | ||
413 | result = i2400m_dev_initialize(i2400m); | ||
414 | if (result < 0) | ||
415 | goto error_dev_initialize; | ||
416 | /* At this point, reports will come for the device and set it | ||
417 | * to the right state if it is different than UNINITIALIZED */ | ||
418 | d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", | ||
419 | net_dev, i2400m, result); | ||
420 | return result; | ||
421 | |||
422 | error_dev_initialize: | ||
423 | error_check_mac_addr: | ||
424 | destroy_workqueue(i2400m->work_queue); | ||
425 | error_create_workqueue: | ||
426 | i2400m->bus_dev_stop(i2400m); | ||
427 | error_bus_dev_start: | ||
428 | i2400m_tx_release(i2400m); | ||
429 | error_tx_setup: | ||
430 | error_bootstrap: | ||
431 | if (result == -ERESTARTSYS && times-- > 0) { | ||
432 | flags = I2400M_BRI_SOFT; | ||
433 | goto retry; | ||
434 | } | ||
435 | d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", | ||
436 | net_dev, i2400m, result); | ||
437 | return result; | ||
438 | } | ||
439 | |||
440 | |||
441 | static | ||
442 | int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) | ||
443 | { | ||
444 | int result; | ||
445 | mutex_lock(&i2400m->init_mutex); /* Well, start the device */ | ||
446 | result = __i2400m_dev_start(i2400m, bm_flags); | ||
447 | if (result >= 0) | ||
448 | i2400m->updown = 1; | ||
449 | mutex_unlock(&i2400m->init_mutex); | ||
450 | return result; | ||
451 | } | ||
452 | |||
453 | |||
454 | /** | ||
455 | * i2400m_dev_stop - Tear down driver communication with the device | ||
456 | * | ||
457 | * @i2400m: device descriptor | ||
458 | * | ||
459 | * Returns: 0 if ok, < 0 errno code on error. | ||
460 | * | ||
461 | * Releases all the resources allocated to communicate with the device. | ||
462 | */ | ||
463 | static | ||
464 | void __i2400m_dev_stop(struct i2400m *i2400m) | ||
465 | { | ||
466 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
467 | struct device *dev = i2400m_dev(i2400m); | ||
468 | |||
469 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
470 | wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); | ||
471 | i2400m_dev_shutdown(i2400m); | ||
472 | i2400m->ready = 0; | ||
473 | destroy_workqueue(i2400m->work_queue); | ||
474 | i2400m->bus_dev_stop(i2400m); | ||
475 | i2400m_tx_release(i2400m); | ||
476 | wimax_state_change(wimax_dev, WIMAX_ST_DOWN); | ||
477 | d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m); | ||
478 | } | ||
479 | |||
480 | |||
481 | /* | ||
482 | * Watch out -- we only need to stop if there is a need for it. The | ||
483 | * device could have reset itself and failed to come up again (see | ||
484 | * _i2400m_dev_reset_handle()). | ||
485 | */ | ||
486 | static | ||
487 | void i2400m_dev_stop(struct i2400m *i2400m) | ||
488 | { | ||
489 | mutex_lock(&i2400m->init_mutex); | ||
490 | if (i2400m->updown) { | ||
491 | __i2400m_dev_stop(i2400m); | ||
492 | i2400m->updown = 0; | ||
493 | } | ||
494 | mutex_unlock(&i2400m->init_mutex); | ||
495 | } | ||
496 | |||
497 | |||
498 | /* | ||
499 | * The device has rebooted; fix up the device and the driver | ||
500 | * | ||
501 | * Tear down the driver communication with the device, reload the | ||
502 | * firmware and reinitialize the communication with the device. | ||
503 | * | ||
504 | * If someone calls a reset when the device's firmware is down, in | ||
505 | * theory we won't see it because we are not listening. However, just | ||
506 | * in case, leave the code to handle it. | ||
507 | * | ||
508 | * If there is a reset context, use it; this means someone is waiting | ||
509 | * for us to tell him when the reset operation is complete and the | ||
510 | * device is ready to rock again. | ||
511 | * | ||
512 | * NOTE: if we are in the process of bringing up or down the | ||
513 | * communication with the device [running i2400m_dev_start() or | ||
514 | * _stop()], don't do anything, let it fail and handle it. | ||
515 | * | ||
516 | * This function is ran always in a thread context | ||
517 | */ | ||
518 | static | ||
519 | void __i2400m_dev_reset_handle(struct work_struct *ws) | ||
520 | { | ||
521 | int result; | ||
522 | struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws); | ||
523 | struct i2400m *i2400m = iw->i2400m; | ||
524 | struct device *dev = i2400m_dev(i2400m); | ||
525 | enum wimax_st wimax_state; | ||
526 | struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; | ||
527 | |||
528 | d_fnstart(3, dev, "(ws %p i2400m %p)\n", ws, i2400m); | ||
529 | result = 0; | ||
530 | if (mutex_trylock(&i2400m->init_mutex) == 0) { | ||
531 | /* We are still in i2400m_dev_start() [let it fail] or | ||
532 | * i2400m_dev_stop() [we are shutting down anyway, so | ||
533 | * ignore it] or we are resetting somewhere else. */ | ||
534 | dev_err(dev, "device rebooted\n"); | ||
535 | i2400m_msg_to_dev_cancel_wait(i2400m, -ERESTARTSYS); | ||
536 | complete(&i2400m->msg_completion); | ||
537 | goto out; | ||
538 | } | ||
539 | wimax_state = wimax_state_get(&i2400m->wimax_dev); | ||
540 | if (wimax_state < WIMAX_ST_UNINITIALIZED) { | ||
541 | dev_info(dev, "device rebooted: it is down, ignoring\n"); | ||
542 | goto out_unlock; /* ifconfig up/down wasn't called */ | ||
543 | } | ||
544 | dev_err(dev, "device rebooted: reinitializing driver\n"); | ||
545 | __i2400m_dev_stop(i2400m); | ||
546 | i2400m->updown = 0; | ||
547 | result = __i2400m_dev_start(i2400m, | ||
548 | I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); | ||
549 | if (result < 0) { | ||
550 | dev_err(dev, "device reboot: cannot start the device: %d\n", | ||
551 | result); | ||
552 | result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); | ||
553 | if (result >= 0) | ||
554 | result = -ENODEV; | ||
555 | } else | ||
556 | i2400m->updown = 1; | ||
557 | out_unlock: | ||
558 | if (i2400m->reset_ctx) { | ||
559 | ctx->result = result; | ||
560 | complete(&ctx->completion); | ||
561 | } | ||
562 | mutex_unlock(&i2400m->init_mutex); | ||
563 | out: | ||
564 | i2400m_put(i2400m); | ||
565 | kfree(iw); | ||
566 | d_fnend(3, dev, "(ws %p i2400m %p) = void\n", ws, i2400m); | ||
567 | return; | ||
568 | } | ||
569 | |||
570 | |||
571 | /** | ||
572 | * i2400m_dev_reset_handle - Handle a device's reset in a thread context | ||
573 | * | ||
574 | * Schedule a device reset handling out on a thread context, so it | ||
575 | * is safe to call from atomic context. We can't use the i2400m's | ||
576 | * queue as we are going to destroy it and reinitialize it as part of | ||
577 | * the driver bringup/bringup process. | ||
578 | * | ||
579 | * See __i2400m_dev_reset_handle() for details; that takes care of | ||
580 | * reinitializing the driver to handle the reset, calling into the | ||
581 | * bus-specific functions ops as needed. | ||
582 | */ | ||
583 | int i2400m_dev_reset_handle(struct i2400m *i2400m) | ||
584 | { | ||
585 | return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, | ||
586 | GFP_ATOMIC); | ||
587 | } | ||
588 | EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); | ||
589 | |||
590 | |||
591 | /** | ||
592 | * i2400m_setup - bus-generic setup function for the i2400m device | ||
593 | * | ||
594 | * @i2400m: device descriptor (bus-specific parts have been initialized) | ||
595 | * | ||
596 | * Returns: 0 if ok, < 0 errno code on error. | ||
597 | * | ||
598 | * Initializes the bus-generic parts of the i2400m driver; the | ||
599 | * bus-specific parts have been initialized, function pointers filled | ||
600 | * out by the bus-specific probe function. | ||
601 | * | ||
602 | * As well, this registers the WiMAX and net device nodes. Once this | ||
603 | * function returns, the device is operative and has to be ready to | ||
604 | * receive and send network traffic and WiMAX control operations. | ||
605 | */ | ||
606 | int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) | ||
607 | { | ||
608 | int result = -ENODEV; | ||
609 | struct device *dev = i2400m_dev(i2400m); | ||
610 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
611 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
612 | |||
613 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
614 | |||
615 | snprintf(wimax_dev->name, sizeof(wimax_dev->name), | ||
616 | "i2400m-%s:%s", dev->bus->name, dev->bus_id); | ||
617 | |||
618 | i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL); | ||
619 | if (i2400m->bm_cmd_buf == NULL) { | ||
620 | dev_err(dev, "cannot allocate USB command buffer\n"); | ||
621 | goto error_bm_cmd_kzalloc; | ||
622 | } | ||
623 | i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL); | ||
624 | if (i2400m->bm_ack_buf == NULL) { | ||
625 | dev_err(dev, "cannot allocate USB ack buffer\n"); | ||
626 | goto error_bm_ack_buf_kzalloc; | ||
627 | } | ||
628 | result = i2400m_bootrom_init(i2400m, bm_flags); | ||
629 | if (result < 0) { | ||
630 | dev_err(dev, "read mac addr: bootrom init " | ||
631 | "failed: %d\n", result); | ||
632 | goto error_bootrom_init; | ||
633 | } | ||
634 | result = i2400m_read_mac_addr(i2400m); | ||
635 | if (result < 0) | ||
636 | goto error_read_mac_addr; | ||
637 | |||
638 | result = register_netdev(net_dev); /* Okey dokey, bring it up */ | ||
639 | if (result < 0) { | ||
640 | dev_err(dev, "cannot register i2400m network device: %d\n", | ||
641 | result); | ||
642 | goto error_register_netdev; | ||
643 | } | ||
644 | netif_carrier_off(net_dev); | ||
645 | |||
646 | result = i2400m_dev_start(i2400m, bm_flags); | ||
647 | if (result < 0) | ||
648 | goto error_dev_start; | ||
649 | |||
650 | i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user; | ||
651 | i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle; | ||
652 | i2400m->wimax_dev.op_reset = i2400m_op_reset; | ||
653 | result = wimax_dev_add(&i2400m->wimax_dev, net_dev); | ||
654 | if (result < 0) | ||
655 | goto error_wimax_dev_add; | ||
656 | /* User space needs to do some init stuff */ | ||
657 | wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); | ||
658 | |||
659 | /* Now setup all that requires a registered net and wimax device. */ | ||
660 | result = i2400m_debugfs_add(i2400m); | ||
661 | if (result < 0) { | ||
662 | dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result); | ||
663 | goto error_debugfs_setup; | ||
664 | } | ||
665 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
666 | return result; | ||
667 | |||
668 | error_debugfs_setup: | ||
669 | wimax_dev_rm(&i2400m->wimax_dev); | ||
670 | error_wimax_dev_add: | ||
671 | i2400m_dev_stop(i2400m); | ||
672 | error_dev_start: | ||
673 | unregister_netdev(net_dev); | ||
674 | error_register_netdev: | ||
675 | error_read_mac_addr: | ||
676 | error_bootrom_init: | ||
677 | kfree(i2400m->bm_ack_buf); | ||
678 | error_bm_ack_buf_kzalloc: | ||
679 | kfree(i2400m->bm_cmd_buf); | ||
680 | error_bm_cmd_kzalloc: | ||
681 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
682 | return result; | ||
683 | } | ||
684 | EXPORT_SYMBOL_GPL(i2400m_setup); | ||
685 | |||
686 | |||
687 | /** | ||
688 | * i2400m_release - release the bus-generic driver resources | ||
689 | * | ||
690 | * Sends a disconnect message and undoes any setup done by i2400m_setup() | ||
691 | */ | ||
692 | void i2400m_release(struct i2400m *i2400m) | ||
693 | { | ||
694 | struct device *dev = i2400m_dev(i2400m); | ||
695 | |||
696 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
697 | netif_stop_queue(i2400m->wimax_dev.net_dev); | ||
698 | |||
699 | i2400m_debugfs_rm(i2400m); | ||
700 | wimax_dev_rm(&i2400m->wimax_dev); | ||
701 | i2400m_dev_stop(i2400m); | ||
702 | unregister_netdev(i2400m->wimax_dev.net_dev); | ||
703 | kfree(i2400m->bm_ack_buf); | ||
704 | kfree(i2400m->bm_cmd_buf); | ||
705 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
706 | } | ||
707 | EXPORT_SYMBOL_GPL(i2400m_release); | ||
708 | |||
709 | |||
710 | static | ||
711 | int __init i2400m_driver_init(void) | ||
712 | { | ||
713 | return 0; | ||
714 | } | ||
715 | module_init(i2400m_driver_init); | ||
716 | |||
717 | static | ||
718 | void __exit i2400m_driver_exit(void) | ||
719 | { | ||
720 | /* for scheds i2400m_dev_reset_handle() */ | ||
721 | flush_scheduled_work(); | ||
722 | return; | ||
723 | } | ||
724 | module_exit(i2400m_driver_exit); | ||
725 | |||
726 | MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); | ||
727 | MODULE_DESCRIPTION("Intel 2400M WiMAX networking bus-generic driver"); | ||
728 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c new file mode 100644 index 000000000000..1d8271f34c38 --- /dev/null +++ b/drivers/net/wimax/i2400m/fw.c | |||
@@ -0,0 +1,1095 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Firmware uploader | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
38 | * - Initial implementation | ||
39 | * | ||
40 | * | ||
41 | * THE PROCEDURE | ||
42 | * | ||
43 | * (this is decribed for USB, but for SDIO is similar) | ||
44 | * | ||
45 | * The 2400m works in two modes: boot-mode or normal mode. In boot | ||
46 | * mode we can execute only a handful of commands targeted at | ||
47 | * uploading the firmware and launching it. | ||
48 | * | ||
49 | * The 2400m enters boot mode when it is first connected to the | ||
50 | * system, when it crashes and when you ask it to reboot. There are | ||
51 | * two submodes of the boot mode: signed and non-signed. Signed takes | ||
52 | * firmwares signed with a certain private key, non-signed takes any | ||
53 | * firmware. Normal hardware takes only signed firmware. | ||
54 | * | ||
55 | * Upon entrance to boot mode, the device sends a few zero length | ||
56 | * packets (ZLPs) on the notification endpoint, then a reboot barker | ||
57 | * (4 le32 words with value I2400M_{S,N}BOOT_BARKER). We ack it by | ||
58 | * sending the same barker on the bulk out endpoint. The device acks | ||
59 | * with a reboot ack barker (4 le32 words with value 0xfeedbabe) and | ||
60 | * then the device is fully rebooted. At this point we can upload the | ||
61 | * firmware. | ||
62 | * | ||
63 | * This process is accomplished by the i2400m_bootrom_init() | ||
64 | * function. All the device interaction happens through the | ||
65 | * i2400m_bm_cmd() [boot mode command]. Special return values will | ||
66 | * indicate if the device resets. | ||
67 | * | ||
68 | * After this, we read the MAC address and then (if needed) | ||
69 | * reinitialize the device. We need to read it ahead of time because | ||
70 | * in the future, we might not upload the firmware until userspace | ||
71 | * 'ifconfig up's the device. | ||
72 | * | ||
73 | * We can then upload the firmware file. The file is composed of a BCF | ||
74 | * header (basic data, keys and signatures) and a list of write | ||
75 | * commands and payloads. We first upload the header | ||
76 | * [i2400m_dnload_init()] and then pass the commands and payloads | ||
77 | * verbatim to the i2400m_bm_cmd() function | ||
78 | * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new | ||
79 | * firmware [i2400m_dnload_finalize()]. | ||
80 | * | ||
81 | * Once firmware is uploaded, we are good to go :) | ||
82 | * | ||
83 | * When we don't know in which mode we are, we first try by sending a | ||
84 | * warm reset request that will take us to boot-mode. If we time out | ||
85 | * waiting for a reboot barker, that means maybe we are already in | ||
86 | * boot mode, so we send a reboot barker. | ||
87 | * | ||
88 | * COMMAND EXECUTION | ||
89 | * | ||
90 | * This code (and process) is single threaded; for executing commands, | ||
91 | * we post a URB to the notification endpoint, post the command, wait | ||
92 | * for data on the notification buffer. We don't need to worry about | ||
93 | * others as we know we are the only ones in there. | ||
94 | * | ||
95 | * BACKEND IMPLEMENTATION | ||
96 | * | ||
97 | * This code is bus-generic; the bus-specific driver provides back end | ||
98 | * implementations to send a boot mode command to the device and to | ||
99 | * read an acknolwedgement from it (or an asynchronous notification) | ||
100 | * from it. | ||
101 | * | ||
102 | * ROADMAP | ||
103 | * | ||
104 | * i2400m_dev_bootstrap Called by __i2400m_dev_start() | ||
105 | * request_firmware | ||
106 | * i2400m_fw_check | ||
107 | * i2400m_fw_dnload | ||
108 | * release_firmware | ||
109 | * | ||
110 | * i2400m_fw_dnload | ||
111 | * i2400m_bootrom_init | ||
112 | * i2400m_bm_cmd | ||
113 | * i2400m->bus_reset | ||
114 | * i2400m_dnload_init | ||
115 | * i2400m_dnload_init_signed | ||
116 | * i2400m_dnload_init_nonsigned | ||
117 | * i2400m_download_chunk | ||
118 | * i2400m_bm_cmd | ||
119 | * i2400m_dnload_bcf | ||
120 | * i2400m_bm_cmd | ||
121 | * i2400m_dnload_finalize | ||
122 | * i2400m_bm_cmd | ||
123 | * | ||
124 | * i2400m_bm_cmd | ||
125 | * i2400m->bus_bm_cmd_send() | ||
126 | * i2400m->bus_bm_wait_for_ack | ||
127 | * __i2400m_bm_ack_verify | ||
128 | * | ||
129 | * i2400m_bm_cmd_prepare Used by bus-drivers to prep | ||
130 | * commands before sending | ||
131 | */ | ||
132 | #include <linux/firmware.h> | ||
133 | #include <linux/sched.h> | ||
134 | #include <linux/usb.h> | ||
135 | #include "i2400m.h" | ||
136 | |||
137 | |||
138 | #define D_SUBMODULE fw | ||
139 | #include "debug-levels.h" | ||
140 | |||
141 | |||
142 | static const __le32 i2400m_ACK_BARKER[4] = { | ||
143 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | ||
144 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | ||
145 | __constant_cpu_to_le32(I2400M_ACK_BARKER), | ||
146 | __constant_cpu_to_le32(I2400M_ACK_BARKER) | ||
147 | }; | ||
148 | |||
149 | |||
150 | /** | ||
151 | * Prepare a boot-mode command for delivery | ||
152 | * | ||
153 | * @cmd: pointer to bootrom header to prepare | ||
154 | * | ||
155 | * Computes checksum if so needed. After calling this function, DO NOT | ||
156 | * modify the command or header as the checksum won't work anymore. | ||
157 | * | ||
158 | * We do it from here because some times we cannot do it in the | ||
159 | * original context the command was sent (it is a const), so when we | ||
160 | * copy it to our staging buffer, we add the checksum there. | ||
161 | */ | ||
162 | void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *cmd) | ||
163 | { | ||
164 | if (i2400m_brh_get_use_checksum(cmd)) { | ||
165 | int i; | ||
166 | u32 checksum = 0; | ||
167 | const u32 *checksum_ptr = (void *) cmd->payload; | ||
168 | for (i = 0; i < cmd->data_size / 4; i++) | ||
169 | checksum += cpu_to_le32(*checksum_ptr++); | ||
170 | checksum += cmd->command + cmd->target_addr + cmd->data_size; | ||
171 | cmd->block_checksum = cpu_to_le32(checksum); | ||
172 | } | ||
173 | } | ||
174 | EXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare); | ||
175 | |||
176 | |||
177 | /* | ||
178 | * Verify the ack data received | ||
179 | * | ||
180 | * Given a reply to a boot mode command, chew it and verify everything | ||
181 | * is ok. | ||
182 | * | ||
183 | * @opcode: opcode which generated this ack. For error messages. | ||
184 | * @ack: pointer to ack data we received | ||
185 | * @ack_size: size of that data buffer | ||
186 | * @flags: I2400M_BM_CMD_* flags we called the command with. | ||
187 | * | ||
188 | * Way too long function -- maybe it should be further split | ||
189 | */ | ||
190 | static | ||
191 | ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode, | ||
192 | struct i2400m_bootrom_header *ack, | ||
193 | size_t ack_size, int flags) | ||
194 | { | ||
195 | ssize_t result = -ENOMEM; | ||
196 | struct device *dev = i2400m_dev(i2400m); | ||
197 | |||
198 | d_fnstart(8, dev, "(i2400m %p opcode %d ack %p size %zu)\n", | ||
199 | i2400m, opcode, ack, ack_size); | ||
200 | if (ack_size < sizeof(*ack)) { | ||
201 | result = -EIO; | ||
202 | dev_err(dev, "boot-mode cmd %d: HW BUG? notification didn't " | ||
203 | "return enough data (%zu bytes vs %zu expected)\n", | ||
204 | opcode, ack_size, sizeof(*ack)); | ||
205 | goto error_ack_short; | ||
206 | } | ||
207 | if (ack_size == sizeof(i2400m_NBOOT_BARKER) | ||
208 | && memcmp(ack, i2400m_NBOOT_BARKER, sizeof(*ack)) == 0) { | ||
209 | result = -ERESTARTSYS; | ||
210 | i2400m->sboot = 0; | ||
211 | d_printf(6, dev, "boot-mode cmd %d: " | ||
212 | "HW non-signed boot barker\n", opcode); | ||
213 | goto error_reboot; | ||
214 | } | ||
215 | if (ack_size == sizeof(i2400m_SBOOT_BARKER) | ||
216 | && memcmp(ack, i2400m_SBOOT_BARKER, sizeof(*ack)) == 0) { | ||
217 | result = -ERESTARTSYS; | ||
218 | i2400m->sboot = 1; | ||
219 | d_printf(6, dev, "boot-mode cmd %d: HW signed reboot barker\n", | ||
220 | opcode); | ||
221 | goto error_reboot; | ||
222 | } | ||
223 | if (ack_size == sizeof(i2400m_ACK_BARKER) | ||
224 | && memcmp(ack, i2400m_ACK_BARKER, sizeof(*ack)) == 0) { | ||
225 | result = -EISCONN; | ||
226 | d_printf(3, dev, "boot-mode cmd %d: HW reboot ack barker\n", | ||
227 | opcode); | ||
228 | goto error_reboot_ack; | ||
229 | } | ||
230 | result = 0; | ||
231 | if (flags & I2400M_BM_CMD_RAW) | ||
232 | goto out_raw; | ||
233 | ack->data_size = le32_to_cpu(ack->data_size); | ||
234 | ack->target_addr = le32_to_cpu(ack->target_addr); | ||
235 | ack->block_checksum = le32_to_cpu(ack->block_checksum); | ||
236 | d_printf(5, dev, "boot-mode cmd %d: notification for opcode %u " | ||
237 | "response %u csum %u rr %u da %u\n", | ||
238 | opcode, i2400m_brh_get_opcode(ack), | ||
239 | i2400m_brh_get_response(ack), | ||
240 | i2400m_brh_get_use_checksum(ack), | ||
241 | i2400m_brh_get_response_required(ack), | ||
242 | i2400m_brh_get_direct_access(ack)); | ||
243 | result = -EIO; | ||
244 | if (i2400m_brh_get_signature(ack) != 0xcbbc) { | ||
245 | dev_err(dev, "boot-mode cmd %d: HW BUG? wrong signature " | ||
246 | "0x%04x\n", opcode, i2400m_brh_get_signature(ack)); | ||
247 | goto error_ack_signature; | ||
248 | } | ||
249 | if (opcode != -1 && opcode != i2400m_brh_get_opcode(ack)) { | ||
250 | dev_err(dev, "boot-mode cmd %d: HW BUG? " | ||
251 | "received response for opcode %u, expected %u\n", | ||
252 | opcode, i2400m_brh_get_opcode(ack), opcode); | ||
253 | goto error_ack_opcode; | ||
254 | } | ||
255 | if (i2400m_brh_get_response(ack) != 0) { /* failed? */ | ||
256 | dev_err(dev, "boot-mode cmd %d: error; hw response %u\n", | ||
257 | opcode, i2400m_brh_get_response(ack)); | ||
258 | goto error_ack_failed; | ||
259 | } | ||
260 | if (ack_size < ack->data_size + sizeof(*ack)) { | ||
261 | dev_err(dev, "boot-mode cmd %d: SW BUG " | ||
262 | "driver provided only %zu bytes for %zu bytes " | ||
263 | "of data\n", opcode, ack_size, | ||
264 | (size_t) le32_to_cpu(ack->data_size) + sizeof(*ack)); | ||
265 | goto error_ack_short_buffer; | ||
266 | } | ||
267 | result = ack_size; | ||
268 | /* Don't you love this stack of empty targets? Well, I don't | ||
269 | * either, but it helps track exactly who comes in here and | ||
270 | * why :) */ | ||
271 | error_ack_short_buffer: | ||
272 | error_ack_failed: | ||
273 | error_ack_opcode: | ||
274 | error_ack_signature: | ||
275 | out_raw: | ||
276 | error_reboot_ack: | ||
277 | error_reboot: | ||
278 | error_ack_short: | ||
279 | d_fnend(8, dev, "(i2400m %p opcode %d ack %p size %zu) = %d\n", | ||
280 | i2400m, opcode, ack, ack_size, (int) result); | ||
281 | return result; | ||
282 | } | ||
283 | |||
284 | |||
285 | /** | ||
286 | * i2400m_bm_cmd - Execute a boot mode command | ||
287 | * | ||
288 | * @cmd: buffer containing the command data (pointing at the header). | ||
289 | * This data can be ANYWHERE (for USB, we will copy it to an | ||
290 | * specific buffer). Make sure everything is in proper little | ||
291 | * endian. | ||
292 | * | ||
293 | * A raw buffer can be also sent, just cast it and set flags to | ||
294 | * I2400M_BM_CMD_RAW. | ||
295 | * | ||
296 | * This function will generate a checksum for you if the | ||
297 | * checksum bit in the command is set (unless I2400M_BM_CMD_RAW | ||
298 | * is set). | ||
299 | * | ||
300 | * You can use the i2400m->bm_cmd_buf to stage your commands and | ||
301 | * send them. | ||
302 | * | ||
303 | * If NULL, no command is sent (we just wait for an ack). | ||
304 | * | ||
305 | * @cmd_size: size of the command. Will be auto padded to the | ||
306 | * bus-specific drivers padding requirements. | ||
307 | * | ||
308 | * @ack: buffer where to place the acknowledgement. If it is a regular | ||
309 | * command response, all fields will be returned with the right, | ||
310 | * native endianess. | ||
311 | * | ||
312 | * You *cannot* use i2400m->bm_ack_buf for this buffer. | ||
313 | * | ||
314 | * @ack_size: size of @ack, 16 aligned; you need to provide at least | ||
315 | * sizeof(*ack) bytes and then enough to contain the return data | ||
316 | * from the command | ||
317 | * | ||
318 | * @flags: see I2400M_BM_CMD_* above. | ||
319 | * | ||
320 | * @returns: bytes received by the notification; if < 0, an errno code | ||
321 | * denoting an error or: | ||
322 | * | ||
323 | * -ERESTARTSYS The device has rebooted | ||
324 | * | ||
325 | * Executes a boot-mode command and waits for a response, doing basic | ||
326 | * validation on it; if a zero length response is received, it retries | ||
327 | * waiting for a response until a non-zero one is received (timing out | ||
328 | * after %I2400M_BOOT_RETRIES retries). | ||
329 | */ | ||
330 | static | ||
331 | ssize_t i2400m_bm_cmd(struct i2400m *i2400m, | ||
332 | const struct i2400m_bootrom_header *cmd, size_t cmd_size, | ||
333 | struct i2400m_bootrom_header *ack, size_t ack_size, | ||
334 | int flags) | ||
335 | { | ||
336 | ssize_t result = -ENOMEM, rx_bytes; | ||
337 | struct device *dev = i2400m_dev(i2400m); | ||
338 | int opcode = cmd == NULL ? -1 : i2400m_brh_get_opcode(cmd); | ||
339 | |||
340 | d_fnstart(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu)\n", | ||
341 | i2400m, cmd, cmd_size, ack, ack_size); | ||
342 | BUG_ON(ack_size < sizeof(*ack)); | ||
343 | BUG_ON(i2400m->boot_mode == 0); | ||
344 | |||
345 | if (cmd != NULL) { /* send the command */ | ||
346 | memcpy(i2400m->bm_cmd_buf, cmd, cmd_size); | ||
347 | result = i2400m->bus_bm_cmd_send(i2400m, cmd, cmd_size, flags); | ||
348 | if (result < 0) | ||
349 | goto error_cmd_send; | ||
350 | if ((flags & I2400M_BM_CMD_RAW) == 0) | ||
351 | d_printf(5, dev, | ||
352 | "boot-mode cmd %d csum %u rr %u da %u: " | ||
353 | "addr 0x%04x size %u block csum 0x%04x\n", | ||
354 | opcode, i2400m_brh_get_use_checksum(cmd), | ||
355 | i2400m_brh_get_response_required(cmd), | ||
356 | i2400m_brh_get_direct_access(cmd), | ||
357 | cmd->target_addr, cmd->data_size, | ||
358 | cmd->block_checksum); | ||
359 | } | ||
360 | result = i2400m->bus_bm_wait_for_ack(i2400m, ack, ack_size); | ||
361 | if (result < 0) { | ||
362 | dev_err(dev, "boot-mode cmd %d: error waiting for an ack: %d\n", | ||
363 | opcode, (int) result); /* bah, %zd doesn't work */ | ||
364 | goto error_wait_for_ack; | ||
365 | } | ||
366 | rx_bytes = result; | ||
367 | /* verify the ack and read more if neccessary [result is the | ||
368 | * final amount of bytes we get in the ack] */ | ||
369 | result = __i2400m_bm_ack_verify(i2400m, opcode, ack, ack_size, flags); | ||
370 | if (result < 0) | ||
371 | goto error_bad_ack; | ||
372 | /* Don't you love this stack of empty targets? Well, I don't | ||
373 | * either, but it helps track exactly who comes in here and | ||
374 | * why :) */ | ||
375 | result = rx_bytes; | ||
376 | error_bad_ack: | ||
377 | error_wait_for_ack: | ||
378 | error_cmd_send: | ||
379 | d_fnend(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu) = %d\n", | ||
380 | i2400m, cmd, cmd_size, ack, ack_size, (int) result); | ||
381 | return result; | ||
382 | } | ||
383 | |||
384 | |||
385 | /** | ||
386 | * i2400m_download_chunk - write a single chunk of data to the device's memory | ||
387 | * | ||
388 | * @i2400m: device descriptor | ||
389 | * @buf: the buffer to write | ||
390 | * @buf_len: length of the buffer to write | ||
391 | * @addr: address in the device memory space | ||
392 | * @direct: bootrom write mode | ||
393 | * @do_csum: should a checksum validation be performed | ||
394 | */ | ||
395 | static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk, | ||
396 | size_t __chunk_len, unsigned long addr, | ||
397 | unsigned int direct, unsigned int do_csum) | ||
398 | { | ||
399 | int ret; | ||
400 | size_t chunk_len = ALIGN(__chunk_len, I2400M_PL_PAD); | ||
401 | struct device *dev = i2400m_dev(i2400m); | ||
402 | struct { | ||
403 | struct i2400m_bootrom_header cmd; | ||
404 | u8 cmd_payload[chunk_len]; | ||
405 | } __attribute__((packed)) *buf; | ||
406 | struct i2400m_bootrom_header ack; | ||
407 | |||
408 | d_fnstart(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx " | ||
409 | "direct %u do_csum %u)\n", i2400m, chunk, __chunk_len, | ||
410 | addr, direct, do_csum); | ||
411 | buf = i2400m->bm_cmd_buf; | ||
412 | memcpy(buf->cmd_payload, chunk, __chunk_len); | ||
413 | memset(buf->cmd_payload + __chunk_len, 0xad, chunk_len - __chunk_len); | ||
414 | |||
415 | buf->cmd.command = i2400m_brh_command(I2400M_BRH_WRITE, | ||
416 | __chunk_len & 0x3 ? 0 : do_csum, | ||
417 | __chunk_len & 0xf ? 0 : direct); | ||
418 | buf->cmd.target_addr = cpu_to_le32(addr); | ||
419 | buf->cmd.data_size = cpu_to_le32(__chunk_len); | ||
420 | ret = i2400m_bm_cmd(i2400m, &buf->cmd, sizeof(buf->cmd) + chunk_len, | ||
421 | &ack, sizeof(ack), 0); | ||
422 | if (ret >= 0) | ||
423 | ret = 0; | ||
424 | d_fnend(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx " | ||
425 | "direct %u do_csum %u) = %d\n", i2400m, chunk, __chunk_len, | ||
426 | addr, direct, do_csum, ret); | ||
427 | return ret; | ||
428 | } | ||
429 | |||
430 | |||
431 | /* | ||
432 | * Download a BCF file's sections to the device | ||
433 | * | ||
434 | * @i2400m: device descriptor | ||
435 | * @bcf: pointer to firmware data (followed by the payloads). Assumed | ||
436 | * verified and consistent. | ||
437 | * @bcf_len: length (in bytes) of the @bcf buffer. | ||
438 | * | ||
439 | * Returns: < 0 errno code on error or the offset to the jump instruction. | ||
440 | * | ||
441 | * Given a BCF file, downloads each section (a command and a payload) | ||
442 | * to the device's address space. Actually, it just executes each | ||
443 | * command i the BCF file. | ||
444 | * | ||
445 | * The section size has to be aligned to 4 bytes AND the padding has | ||
446 | * to be taken from the firmware file, as the signature takes it into | ||
447 | * account. | ||
448 | */ | ||
449 | static | ||
450 | ssize_t i2400m_dnload_bcf(struct i2400m *i2400m, | ||
451 | const struct i2400m_bcf_hdr *bcf, size_t bcf_len) | ||
452 | { | ||
453 | ssize_t ret; | ||
454 | struct device *dev = i2400m_dev(i2400m); | ||
455 | size_t offset, /* iterator offset */ | ||
456 | data_size, /* Size of the data payload */ | ||
457 | section_size, /* Size of the whole section (cmd + payload) */ | ||
458 | section = 1; | ||
459 | const struct i2400m_bootrom_header *bh; | ||
460 | struct i2400m_bootrom_header ack; | ||
461 | |||
462 | d_fnstart(3, dev, "(i2400m %p bcf %p bcf_len %zu)\n", | ||
463 | i2400m, bcf, bcf_len); | ||
464 | /* Iterate over the command blocks in the BCF file that start | ||
465 | * after the header */ | ||
466 | offset = le32_to_cpu(bcf->header_len) * sizeof(u32); | ||
467 | while (1) { /* start sending the file */ | ||
468 | bh = (void *) bcf + offset; | ||
469 | data_size = le32_to_cpu(bh->data_size); | ||
470 | section_size = ALIGN(sizeof(*bh) + data_size, 4); | ||
471 | d_printf(7, dev, | ||
472 | "downloading section #%zu (@%zu %zu B) to 0x%08x\n", | ||
473 | section, offset, sizeof(*bh) + data_size, | ||
474 | le32_to_cpu(bh->target_addr)); | ||
475 | if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP) { | ||
476 | /* Secure boot needs to stop here */ | ||
477 | d_printf(5, dev, "signed jump found @%zu\n", offset); | ||
478 | break; | ||
479 | } | ||
480 | if (offset + section_size == bcf_len) | ||
481 | /* Non-secure boot stops here */ | ||
482 | break; | ||
483 | if (offset + section_size > bcf_len) { | ||
484 | dev_err(dev, "fw %s: bad section #%zu, " | ||
485 | "end (@%zu) beyond EOF (@%zu)\n", | ||
486 | i2400m->bus_fw_name, section, | ||
487 | offset + section_size, bcf_len); | ||
488 | ret = -EINVAL; | ||
489 | goto error_section_beyond_eof; | ||
490 | } | ||
491 | __i2400m_msleep(20); | ||
492 | ret = i2400m_bm_cmd(i2400m, bh, section_size, | ||
493 | &ack, sizeof(ack), I2400M_BM_CMD_RAW); | ||
494 | if (ret < 0) { | ||
495 | dev_err(dev, "fw %s: section #%zu (@%zu %zu B) " | ||
496 | "failed %d\n", i2400m->bus_fw_name, section, | ||
497 | offset, sizeof(*bh) + data_size, (int) ret); | ||
498 | goto error_send; | ||
499 | } | ||
500 | offset += section_size; | ||
501 | section++; | ||
502 | } | ||
503 | ret = offset; | ||
504 | error_section_beyond_eof: | ||
505 | error_send: | ||
506 | d_fnend(3, dev, "(i2400m %p bcf %p bcf_len %zu) = %d\n", | ||
507 | i2400m, bcf, bcf_len, (int) ret); | ||
508 | return ret; | ||
509 | } | ||
510 | |||
511 | |||
512 | /* | ||
513 | * Do the final steps of uploading firmware | ||
514 | * | ||
515 | * Depending on the boot mode (signed vs non-signed), different | ||
516 | * actions need to be taken. | ||
517 | */ | ||
518 | static | ||
519 | int i2400m_dnload_finalize(struct i2400m *i2400m, | ||
520 | const struct i2400m_bcf_hdr *bcf, size_t offset) | ||
521 | { | ||
522 | int ret = 0; | ||
523 | struct device *dev = i2400m_dev(i2400m); | ||
524 | struct i2400m_bootrom_header *cmd, ack; | ||
525 | struct { | ||
526 | struct i2400m_bootrom_header cmd; | ||
527 | u8 cmd_pl[0]; | ||
528 | } __attribute__((packed)) *cmd_buf; | ||
529 | size_t signature_block_offset, signature_block_size; | ||
530 | |||
531 | d_fnstart(3, dev, "offset %zu\n", offset); | ||
532 | cmd = (void *) bcf + offset; | ||
533 | if (i2400m->sboot == 0) { | ||
534 | struct i2400m_bootrom_header jump_ack; | ||
535 | d_printf(3, dev, "unsecure boot, jumping to 0x%08x\n", | ||
536 | le32_to_cpu(cmd->target_addr)); | ||
537 | i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP); | ||
538 | cmd->data_size = 0; | ||
539 | ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), | ||
540 | &jump_ack, sizeof(jump_ack), 0); | ||
541 | } else { | ||
542 | d_printf(3, dev, "secure boot, jumping to 0x%08x\n", | ||
543 | le32_to_cpu(cmd->target_addr)); | ||
544 | cmd_buf = i2400m->bm_cmd_buf; | ||
545 | memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); | ||
546 | signature_block_offset = | ||
547 | sizeof(*bcf) | ||
548 | + le32_to_cpu(bcf->key_size) * sizeof(u32) | ||
549 | + le32_to_cpu(bcf->exponent_size) * sizeof(u32); | ||
550 | signature_block_size = | ||
551 | le32_to_cpu(bcf->modulus_size) * sizeof(u32); | ||
552 | memcpy(cmd_buf->cmd_pl, (void *) bcf + signature_block_offset, | ||
553 | signature_block_size); | ||
554 | ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, | ||
555 | sizeof(cmd_buf->cmd) + signature_block_size, | ||
556 | &ack, sizeof(ack), I2400M_BM_CMD_RAW); | ||
557 | } | ||
558 | d_fnend(3, dev, "returning %d\n", ret); | ||
559 | return ret; | ||
560 | } | ||
561 | |||
562 | |||
563 | /** | ||
564 | * i2400m_bootrom_init - Reboots a powered device into boot mode | ||
565 | * | ||
566 | * @i2400m: device descriptor | ||
567 | * @flags: | ||
568 | * I2400M_BRI_SOFT: a reboot notification has been seen | ||
569 | * already, so don't wait for it. | ||
570 | * | ||
571 | * I2400M_BRI_NO_REBOOT: Don't send a reboot command, but wait | ||
572 | * for a reboot barker notification. This is a one shot; if | ||
573 | * the state machine needs to send a reboot command it will. | ||
574 | * | ||
575 | * Returns: | ||
576 | * | ||
577 | * < 0 errno code on error, 0 if ok. | ||
578 | * | ||
579 | * i2400m->sboot set to 0 for unsecure boot process, 1 for secure | ||
580 | * boot process. | ||
581 | * | ||
582 | * Description: | ||
583 | * | ||
584 | * Tries hard enough to put the device in boot-mode. There are two | ||
585 | * main phases to this: | ||
586 | * | ||
587 | * a. (1) send a reboot command and (2) get a reboot barker | ||
588 | * b. (1) ack the reboot sending a reboot barker and (2) getting an | ||
589 | * ack barker in return | ||
590 | * | ||
591 | * We want to skip (a) in some cases [soft]. The state machine is | ||
592 | * horrible, but it is basically: on each phase, send what has to be | ||
593 | * sent (if any), wait for the answer and act on the answer. We might | ||
594 | * have to backtrack and retry, so we keep a max tries counter for | ||
595 | * that. | ||
596 | * | ||
597 | * If we get a timeout after sending a warm reset, we do it again. | ||
598 | */ | ||
599 | int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) | ||
600 | { | ||
601 | int result; | ||
602 | struct device *dev = i2400m_dev(i2400m); | ||
603 | struct i2400m_bootrom_header *cmd; | ||
604 | struct i2400m_bootrom_header ack; | ||
605 | int count = I2400M_BOOT_RETRIES; | ||
606 | int ack_timeout_cnt = 1; | ||
607 | |||
608 | BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_NBOOT_BARKER)); | ||
609 | BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER)); | ||
610 | |||
611 | d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags); | ||
612 | result = -ENOMEM; | ||
613 | cmd = i2400m->bm_cmd_buf; | ||
614 | if (flags & I2400M_BRI_SOFT) | ||
615 | goto do_reboot_ack; | ||
616 | do_reboot: | ||
617 | if (--count < 0) | ||
618 | goto error_timeout; | ||
619 | d_printf(4, dev, "device reboot: reboot command [%d # left]\n", | ||
620 | count); | ||
621 | if ((flags & I2400M_BRI_NO_REBOOT) == 0) | ||
622 | i2400m->bus_reset(i2400m, I2400M_RT_WARM); | ||
623 | result = i2400m_bm_cmd(i2400m, NULL, 0, &ack, sizeof(ack), | ||
624 | I2400M_BM_CMD_RAW); | ||
625 | flags &= ~I2400M_BRI_NO_REBOOT; | ||
626 | switch (result) { | ||
627 | case -ERESTARTSYS: | ||
628 | d_printf(4, dev, "device reboot: got reboot barker\n"); | ||
629 | break; | ||
630 | case -EISCONN: /* we don't know how it got here...but we follow it */ | ||
631 | d_printf(4, dev, "device reboot: got ack barker - whatever\n"); | ||
632 | goto do_reboot; | ||
633 | case -ETIMEDOUT: /* device has timed out, we might be in boot | ||
634 | * mode already and expecting an ack, let's try | ||
635 | * that */ | ||
636 | dev_info(dev, "warm reset timed out, trying an ack\n"); | ||
637 | goto do_reboot_ack; | ||
638 | case -EPROTO: | ||
639 | case -ESHUTDOWN: /* dev is gone */ | ||
640 | case -EINTR: /* user cancelled */ | ||
641 | goto error_dev_gone; | ||
642 | default: | ||
643 | dev_err(dev, "device reboot: error %d while waiting " | ||
644 | "for reboot barker - rebooting\n", result); | ||
645 | goto do_reboot; | ||
646 | } | ||
647 | /* At this point we ack back with 4 REBOOT barkers and expect | ||
648 | * 4 ACK barkers. This is ugly, as we send a raw command -- | ||
649 | * hence the cast. _bm_cmd() will catch the reboot ack | ||
650 | * notification and report it as -EISCONN. */ | ||
651 | do_reboot_ack: | ||
652 | d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count); | ||
653 | if (i2400m->sboot == 0) | ||
654 | memcpy(cmd, i2400m_NBOOT_BARKER, | ||
655 | sizeof(i2400m_NBOOT_BARKER)); | ||
656 | else | ||
657 | memcpy(cmd, i2400m_SBOOT_BARKER, | ||
658 | sizeof(i2400m_SBOOT_BARKER)); | ||
659 | result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), | ||
660 | &ack, sizeof(ack), I2400M_BM_CMD_RAW); | ||
661 | switch (result) { | ||
662 | case -ERESTARTSYS: | ||
663 | d_printf(4, dev, "reboot ack: got reboot barker - retrying\n"); | ||
664 | if (--count < 0) | ||
665 | goto error_timeout; | ||
666 | goto do_reboot_ack; | ||
667 | case -EISCONN: | ||
668 | d_printf(4, dev, "reboot ack: got ack barker - good\n"); | ||
669 | break; | ||
670 | case -ETIMEDOUT: /* no response, maybe it is the other type? */ | ||
671 | if (ack_timeout_cnt-- >= 0) { | ||
672 | d_printf(4, dev, "reboot ack timedout: " | ||
673 | "trying the other type?\n"); | ||
674 | i2400m->sboot = !i2400m->sboot; | ||
675 | goto do_reboot_ack; | ||
676 | } else { | ||
677 | dev_err(dev, "reboot ack timedout too long: " | ||
678 | "trying reboot\n"); | ||
679 | goto do_reboot; | ||
680 | } | ||
681 | break; | ||
682 | case -EPROTO: | ||
683 | case -ESHUTDOWN: /* dev is gone */ | ||
684 | goto error_dev_gone; | ||
685 | default: | ||
686 | dev_err(dev, "device reboot ack: error %d while waiting for " | ||
687 | "reboot ack barker - rebooting\n", result); | ||
688 | goto do_reboot; | ||
689 | } | ||
690 | d_printf(2, dev, "device reboot ack: got ack barker - boot done\n"); | ||
691 | result = 0; | ||
692 | exit_timeout: | ||
693 | error_dev_gone: | ||
694 | d_fnend(4, dev, "(i2400m %p flags 0x%08x) = %d\n", | ||
695 | i2400m, flags, result); | ||
696 | return result; | ||
697 | |||
698 | error_timeout: | ||
699 | dev_err(dev, "Timed out waiting for reboot ack, resetting\n"); | ||
700 | i2400m->bus_reset(i2400m, I2400M_RT_BUS); | ||
701 | result = -ETIMEDOUT; | ||
702 | goto exit_timeout; | ||
703 | } | ||
704 | |||
705 | |||
706 | /* | ||
707 | * Read the MAC addr | ||
708 | * | ||
709 | * The position this function reads is fixed in device memory and | ||
710 | * always available, even without firmware. | ||
711 | * | ||
712 | * Note we specify we want to read only six bytes, but provide space | ||
713 | * for 16, as we always get it rounded up. | ||
714 | */ | ||
715 | int i2400m_read_mac_addr(struct i2400m *i2400m) | ||
716 | { | ||
717 | int result; | ||
718 | struct device *dev = i2400m_dev(i2400m); | ||
719 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
720 | struct i2400m_bootrom_header *cmd; | ||
721 | struct { | ||
722 | struct i2400m_bootrom_header ack; | ||
723 | u8 ack_pl[16]; | ||
724 | } __attribute__((packed)) ack_buf; | ||
725 | |||
726 | d_fnstart(5, dev, "(i2400m %p)\n", i2400m); | ||
727 | cmd = i2400m->bm_cmd_buf; | ||
728 | cmd->command = i2400m_brh_command(I2400M_BRH_READ, 0, 1); | ||
729 | cmd->target_addr = cpu_to_le32(0x00203fe8); | ||
730 | cmd->data_size = cpu_to_le32(6); | ||
731 | result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), | ||
732 | &ack_buf.ack, sizeof(ack_buf), 0); | ||
733 | if (result < 0) { | ||
734 | dev_err(dev, "BM: read mac addr failed: %d\n", result); | ||
735 | goto error_read_mac; | ||
736 | } | ||
737 | d_printf(2, dev, | ||
738 | "mac addr is %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
739 | ack_buf.ack_pl[0], ack_buf.ack_pl[1], | ||
740 | ack_buf.ack_pl[2], ack_buf.ack_pl[3], | ||
741 | ack_buf.ack_pl[4], ack_buf.ack_pl[5]); | ||
742 | if (i2400m->bus_bm_mac_addr_impaired == 1) { | ||
743 | ack_buf.ack_pl[0] = 0x00; | ||
744 | ack_buf.ack_pl[1] = 0x16; | ||
745 | ack_buf.ack_pl[2] = 0xd3; | ||
746 | get_random_bytes(&ack_buf.ack_pl[3], 3); | ||
747 | dev_err(dev, "BM is MAC addr impaired, faking MAC addr to " | ||
748 | "mac addr is %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
749 | ack_buf.ack_pl[0], ack_buf.ack_pl[1], | ||
750 | ack_buf.ack_pl[2], ack_buf.ack_pl[3], | ||
751 | ack_buf.ack_pl[4], ack_buf.ack_pl[5]); | ||
752 | result = 0; | ||
753 | } | ||
754 | net_dev->addr_len = ETH_ALEN; | ||
755 | memcpy(net_dev->perm_addr, ack_buf.ack_pl, ETH_ALEN); | ||
756 | memcpy(net_dev->dev_addr, ack_buf.ack_pl, ETH_ALEN); | ||
757 | error_read_mac: | ||
758 | d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
759 | return result; | ||
760 | } | ||
761 | |||
762 | |||
763 | /* | ||
764 | * Initialize a non signed boot | ||
765 | * | ||
766 | * This implies sending some magic values to the device's memory. Note | ||
767 | * we convert the values to little endian in the same array | ||
768 | * declaration. | ||
769 | */ | ||
770 | static | ||
771 | int i2400m_dnload_init_nonsigned(struct i2400m *i2400m) | ||
772 | { | ||
773 | #define POKE(a, d) { \ | ||
774 | .address = __constant_cpu_to_le32(a), \ | ||
775 | .data = __constant_cpu_to_le32(d) \ | ||
776 | } | ||
777 | static const struct { | ||
778 | __le32 address; | ||
779 | __le32 data; | ||
780 | } i2400m_pokes[] = { | ||
781 | POKE(0x081A58, 0xA7810230), | ||
782 | POKE(0x080040, 0x00000000), | ||
783 | POKE(0x080048, 0x00000082), | ||
784 | POKE(0x08004C, 0x0000081F), | ||
785 | POKE(0x080054, 0x00000085), | ||
786 | POKE(0x080058, 0x00000180), | ||
787 | POKE(0x08005C, 0x00000018), | ||
788 | POKE(0x080060, 0x00000010), | ||
789 | POKE(0x080574, 0x00000001), | ||
790 | POKE(0x080550, 0x00000005), | ||
791 | POKE(0xAE0000, 0x00000000), | ||
792 | }; | ||
793 | #undef POKE | ||
794 | unsigned i; | ||
795 | int ret; | ||
796 | struct device *dev = i2400m_dev(i2400m); | ||
797 | |||
798 | dev_warn(dev, "WARNING!!! non-signed boot UNTESTED PATH!\n"); | ||
799 | |||
800 | d_fnstart(5, dev, "(i2400m %p)\n", i2400m); | ||
801 | for (i = 0; i < ARRAY_SIZE(i2400m_pokes); i++) { | ||
802 | ret = i2400m_download_chunk(i2400m, &i2400m_pokes[i].data, | ||
803 | sizeof(i2400m_pokes[i].data), | ||
804 | i2400m_pokes[i].address, 1, 1); | ||
805 | if (ret < 0) | ||
806 | break; | ||
807 | } | ||
808 | d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); | ||
809 | return ret; | ||
810 | } | ||
811 | |||
812 | |||
813 | /* | ||
814 | * Initialize the signed boot process | ||
815 | * | ||
816 | * @i2400m: device descriptor | ||
817 | * | ||
818 | * @bcf_hdr: pointer to the firmware header; assumes it is fully in | ||
819 | * memory (it has gone through basic validation). | ||
820 | * | ||
821 | * Returns: 0 if ok, < 0 errno code on error, -ERESTARTSYS if the hw | ||
822 | * rebooted. | ||
823 | * | ||
824 | * This writes the firmware BCF header to the device using the | ||
825 | * HASH_PAYLOAD_ONLY command. | ||
826 | */ | ||
827 | static | ||
828 | int i2400m_dnload_init_signed(struct i2400m *i2400m, | ||
829 | const struct i2400m_bcf_hdr *bcf_hdr) | ||
830 | { | ||
831 | int ret; | ||
832 | struct device *dev = i2400m_dev(i2400m); | ||
833 | struct { | ||
834 | struct i2400m_bootrom_header cmd; | ||
835 | struct i2400m_bcf_hdr cmd_pl; | ||
836 | } __attribute__((packed)) *cmd_buf; | ||
837 | struct i2400m_bootrom_header ack; | ||
838 | |||
839 | d_fnstart(5, dev, "(i2400m %p bcf_hdr %p)\n", i2400m, bcf_hdr); | ||
840 | cmd_buf = i2400m->bm_cmd_buf; | ||
841 | cmd_buf->cmd.command = | ||
842 | i2400m_brh_command(I2400M_BRH_HASH_PAYLOAD_ONLY, 0, 0); | ||
843 | cmd_buf->cmd.target_addr = 0; | ||
844 | cmd_buf->cmd.data_size = cpu_to_le32(sizeof(cmd_buf->cmd_pl)); | ||
845 | memcpy(&cmd_buf->cmd_pl, bcf_hdr, sizeof(*bcf_hdr)); | ||
846 | ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, sizeof(*cmd_buf), | ||
847 | &ack, sizeof(ack), 0); | ||
848 | if (ret >= 0) | ||
849 | ret = 0; | ||
850 | d_fnend(5, dev, "(i2400m %p bcf_hdr %p) = %d\n", i2400m, bcf_hdr, ret); | ||
851 | return ret; | ||
852 | } | ||
853 | |||
854 | |||
855 | /* | ||
856 | * Initialize the firmware download at the device size | ||
857 | * | ||
858 | * Multiplex to the one that matters based on the device's mode | ||
859 | * (signed or non-signed). | ||
860 | */ | ||
861 | static | ||
862 | int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) | ||
863 | { | ||
864 | int result; | ||
865 | struct device *dev = i2400m_dev(i2400m); | ||
866 | u32 module_id = le32_to_cpu(bcf->module_id); | ||
867 | |||
868 | if (i2400m->sboot == 0 | ||
869 | && (module_id & I2400M_BCF_MOD_ID_POKES) == 0) { | ||
870 | /* non-signed boot process without pokes */ | ||
871 | result = i2400m_dnload_init_nonsigned(i2400m); | ||
872 | if (result == -ERESTARTSYS) | ||
873 | return result; | ||
874 | if (result < 0) | ||
875 | dev_err(dev, "fw %s: non-signed download " | ||
876 | "initialization failed: %d\n", | ||
877 | i2400m->bus_fw_name, result); | ||
878 | } else if (i2400m->sboot == 0 | ||
879 | && (module_id & I2400M_BCF_MOD_ID_POKES)) { | ||
880 | /* non-signed boot process with pokes, nothing to do */ | ||
881 | result = 0; | ||
882 | } else { /* signed boot process */ | ||
883 | result = i2400m_dnload_init_signed(i2400m, bcf); | ||
884 | if (result == -ERESTARTSYS) | ||
885 | return result; | ||
886 | if (result < 0) | ||
887 | dev_err(dev, "fw %s: signed boot download " | ||
888 | "initialization failed: %d\n", | ||
889 | i2400m->bus_fw_name, result); | ||
890 | } | ||
891 | return result; | ||
892 | } | ||
893 | |||
894 | |||
895 | /* | ||
896 | * Run quick consistency tests on the firmware file | ||
897 | * | ||
898 | * Check for the firmware being made for the i2400m device, | ||
899 | * etc...These checks are mostly informative, as the device will make | ||
900 | * them too; but the driver's response is more informative on what | ||
901 | * went wrong. | ||
902 | */ | ||
903 | static | ||
904 | int i2400m_fw_check(struct i2400m *i2400m, | ||
905 | const struct i2400m_bcf_hdr *bcf, | ||
906 | size_t bcf_size) | ||
907 | { | ||
908 | int result; | ||
909 | struct device *dev = i2400m_dev(i2400m); | ||
910 | unsigned module_type, header_len, major_version, minor_version, | ||
911 | module_id, module_vendor, date, size; | ||
912 | |||
913 | /* Check hard errors */ | ||
914 | result = -EINVAL; | ||
915 | if (bcf_size < sizeof(*bcf)) { /* big enough header? */ | ||
916 | dev_err(dev, "firmware %s too short: " | ||
917 | "%zu B vs %zu (at least) expected\n", | ||
918 | i2400m->bus_fw_name, bcf_size, sizeof(*bcf)); | ||
919 | goto error; | ||
920 | } | ||
921 | |||
922 | module_type = bcf->module_type; | ||
923 | header_len = sizeof(u32) * le32_to_cpu(bcf->header_len); | ||
924 | major_version = le32_to_cpu(bcf->header_version) & 0xffff0000 >> 16; | ||
925 | minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff; | ||
926 | module_id = le32_to_cpu(bcf->module_id); | ||
927 | module_vendor = le32_to_cpu(bcf->module_vendor); | ||
928 | date = le32_to_cpu(bcf->date); | ||
929 | size = sizeof(u32) * le32_to_cpu(bcf->size); | ||
930 | |||
931 | if (bcf_size != size) { /* annoyingly paranoid */ | ||
932 | dev_err(dev, "firmware %s: bad size, got " | ||
933 | "%zu B vs %u expected\n", | ||
934 | i2400m->bus_fw_name, bcf_size, size); | ||
935 | goto error; | ||
936 | } | ||
937 | |||
938 | d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) " | ||
939 | "date %08x (%zu B)\n", | ||
940 | module_type, module_id, module_vendor, | ||
941 | major_version, minor_version, (size_t) header_len, | ||
942 | date, (size_t) size); | ||
943 | |||
944 | if (module_type != 6) { /* built for the right hardware? */ | ||
945 | dev_err(dev, "bad fw %s: unexpected module type 0x%x; " | ||
946 | "aborting\n", i2400m->bus_fw_name, module_type); | ||
947 | goto error; | ||
948 | } | ||
949 | |||
950 | /* Check soft-er errors */ | ||
951 | result = 0; | ||
952 | if (module_vendor != 0x8086) | ||
953 | dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n", | ||
954 | i2400m->bus_fw_name, module_vendor); | ||
955 | if (date < 0x20080300) | ||
956 | dev_err(dev, "bad fw %s? build date too old %08x\n", | ||
957 | i2400m->bus_fw_name, date); | ||
958 | error: | ||
959 | return result; | ||
960 | } | ||
961 | |||
962 | |||
963 | /* | ||
964 | * Download the firmware to the device | ||
965 | * | ||
966 | * @i2400m: device descriptor | ||
967 | * @bcf: pointer to loaded (and minimally verified for consistency) | ||
968 | * firmware | ||
969 | * @bcf_size: size of the @bcf buffer (header plus payloads) | ||
970 | * | ||
971 | * The process for doing this is described in this file's header. | ||
972 | * | ||
973 | * Note we only reinitialize boot-mode if the flags say so. Some hw | ||
974 | * iterations need it, some don't. In any case, if we loop, we always | ||
975 | * need to reinitialize the boot room, hence the flags modification. | ||
976 | */ | ||
977 | static | ||
978 | int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf, | ||
979 | size_t bcf_size, enum i2400m_bri flags) | ||
980 | { | ||
981 | int ret = 0; | ||
982 | struct device *dev = i2400m_dev(i2400m); | ||
983 | int count = I2400M_BOOT_RETRIES; | ||
984 | |||
985 | d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n", | ||
986 | i2400m, bcf, bcf_size); | ||
987 | i2400m->boot_mode = 1; | ||
988 | hw_reboot: | ||
989 | if (count-- == 0) { | ||
990 | ret = -ERESTARTSYS; | ||
991 | dev_err(dev, "device rebooted too many times, aborting\n"); | ||
992 | goto error_too_many_reboots; | ||
993 | } | ||
994 | if (flags & I2400M_BRI_MAC_REINIT) { | ||
995 | ret = i2400m_bootrom_init(i2400m, flags); | ||
996 | if (ret < 0) { | ||
997 | dev_err(dev, "bootrom init failed: %d\n", ret); | ||
998 | goto error_bootrom_init; | ||
999 | } | ||
1000 | } | ||
1001 | flags |= I2400M_BRI_MAC_REINIT; | ||
1002 | |||
1003 | /* | ||
1004 | * Initialize the download, push the bytes to the device and | ||
1005 | * then jump to the new firmware. Note @ret is passed with the | ||
1006 | * offset of the jump instruction to _dnload_finalize() | ||
1007 | */ | ||
1008 | ret = i2400m_dnload_init(i2400m, bcf); /* Init device's dnload */ | ||
1009 | if (ret == -ERESTARTSYS) | ||
1010 | goto error_dev_rebooted; | ||
1011 | if (ret < 0) | ||
1012 | goto error_dnload_init; | ||
1013 | |||
1014 | ret = i2400m_dnload_bcf(i2400m, bcf, bcf_size); | ||
1015 | if (ret == -ERESTARTSYS) | ||
1016 | goto error_dev_rebooted; | ||
1017 | if (ret < 0) { | ||
1018 | dev_err(dev, "fw %s: download failed: %d\n", | ||
1019 | i2400m->bus_fw_name, ret); | ||
1020 | goto error_dnload_bcf; | ||
1021 | } | ||
1022 | |||
1023 | ret = i2400m_dnload_finalize(i2400m, bcf, ret); | ||
1024 | if (ret == -ERESTARTSYS) | ||
1025 | goto error_dev_rebooted; | ||
1026 | if (ret < 0) { | ||
1027 | dev_err(dev, "fw %s: " | ||
1028 | "download finalization failed: %d\n", | ||
1029 | i2400m->bus_fw_name, ret); | ||
1030 | goto error_dnload_finalize; | ||
1031 | } | ||
1032 | |||
1033 | d_printf(2, dev, "fw %s successfully uploaded\n", | ||
1034 | i2400m->bus_fw_name); | ||
1035 | i2400m->boot_mode = 0; | ||
1036 | error_dnload_finalize: | ||
1037 | error_dnload_bcf: | ||
1038 | error_dnload_init: | ||
1039 | error_bootrom_init: | ||
1040 | error_too_many_reboots: | ||
1041 | d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n", | ||
1042 | i2400m, bcf, bcf_size, ret); | ||
1043 | return ret; | ||
1044 | |||
1045 | error_dev_rebooted: | ||
1046 | dev_err(dev, "device rebooted, %d tries left\n", count); | ||
1047 | /* we got the notification already, no need to wait for it again */ | ||
1048 | flags |= I2400M_BRI_SOFT; | ||
1049 | goto hw_reboot; | ||
1050 | } | ||
1051 | |||
1052 | |||
1053 | /** | ||
1054 | * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware | ||
1055 | * | ||
1056 | * @i2400m: device descriptor | ||
1057 | * | ||
1058 | * Returns: >= 0 if ok, < 0 errno code on error. | ||
1059 | * | ||
1060 | * This sets up the firmware upload environment, loads the firmware | ||
1061 | * file from disk, verifies and then calls the firmware upload process | ||
1062 | * per se. | ||
1063 | * | ||
1064 | * Can be called either from probe, or after a warm reset. Can not be | ||
1065 | * called from within an interrupt. All the flow in this code is | ||
1066 | * single-threade; all I/Os are synchronous. | ||
1067 | */ | ||
1068 | int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) | ||
1069 | { | ||
1070 | int ret = 0; | ||
1071 | struct device *dev = i2400m_dev(i2400m); | ||
1072 | const struct firmware *fw; | ||
1073 | const struct i2400m_bcf_hdr *bcf; /* Firmware data */ | ||
1074 | |||
1075 | d_fnstart(5, dev, "(i2400m %p)\n", i2400m); | ||
1076 | /* Load firmware files to memory. */ | ||
1077 | ret = request_firmware(&fw, i2400m->bus_fw_name, dev); | ||
1078 | if (ret) { | ||
1079 | dev_err(dev, "fw %s: request failed: %d\n", | ||
1080 | i2400m->bus_fw_name, ret); | ||
1081 | goto error_fw_req; | ||
1082 | } | ||
1083 | bcf = (void *) fw->data; | ||
1084 | |||
1085 | ret = i2400m_fw_check(i2400m, bcf, fw->size); | ||
1086 | if (ret < 0) | ||
1087 | goto error_fw_bad; | ||
1088 | ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); | ||
1089 | error_fw_bad: | ||
1090 | release_firmware(fw); | ||
1091 | error_fw_req: | ||
1092 | d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); | ||
1093 | return ret; | ||
1094 | } | ||
1095 | EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap); | ||
diff --git a/drivers/net/wimax/i2400m/i2400m-sdio.h b/drivers/net/wimax/i2400m/i2400m-sdio.h new file mode 100644 index 000000000000..08c2fb739234 --- /dev/null +++ b/drivers/net/wimax/i2400m/i2400m-sdio.h | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * SDIO-specific i2400m driver definitions | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Brian Bian <brian.bian@intel.com> | ||
37 | * Dirk Brandewie <dirk.j.brandewie@intel.com> | ||
38 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
39 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
40 | * - Initial implementation | ||
41 | * | ||
42 | * | ||
43 | * This driver implements the bus-specific part of the i2400m for | ||
44 | * SDIO. Check i2400m.h for a generic driver description. | ||
45 | * | ||
46 | * ARCHITECTURE | ||
47 | * | ||
48 | * This driver sits under the bus-generic i2400m driver, providing the | ||
49 | * connection to the device. | ||
50 | * | ||
51 | * When probed, all the function pointers are setup and then the | ||
52 | * bus-generic code called. The generic driver will then use the | ||
53 | * provided pointers for uploading firmware (i2400ms_bus_bm*() in | ||
54 | * sdio-fw.c) and then setting up the device (i2400ms_dev_*() in | ||
55 | * sdio.c). | ||
56 | * | ||
57 | * Once firmware is uploaded, TX functions (sdio-tx.c) are called when | ||
58 | * data is ready for transmission in the TX fifo; then the SDIO IRQ is | ||
59 | * fired and data is available (sdio-rx.c), it is sent to the generic | ||
60 | * driver for processing with i2400m_rx. | ||
61 | */ | ||
62 | |||
63 | #ifndef __I2400M_SDIO_H__ | ||
64 | #define __I2400M_SDIO_H__ | ||
65 | |||
66 | #include "i2400m.h" | ||
67 | |||
68 | /* Host-Device interface for SDIO */ | ||
69 | enum { | ||
70 | I2400MS_BLK_SIZE = 256, | ||
71 | I2400MS_PL_SIZE_MAX = 0x3E00, | ||
72 | |||
73 | I2400MS_DATA_ADDR = 0x0, | ||
74 | I2400MS_INTR_STATUS_ADDR = 0x13, | ||
75 | I2400MS_INTR_CLEAR_ADDR = 0x13, | ||
76 | I2400MS_INTR_ENABLE_ADDR = 0x14, | ||
77 | I2400MS_INTR_GET_SIZE_ADDR = 0x2C, | ||
78 | /* The number of ticks to wait for the device to signal that | ||
79 | * it is ready */ | ||
80 | I2400MS_INIT_SLEEP_INTERVAL = 10, | ||
81 | }; | ||
82 | |||
83 | |||
84 | /** | ||
85 | * struct i2400ms - descriptor for a SDIO connected i2400m | ||
86 | * | ||
87 | * @i2400m: bus-generic i2400m implementation; has to be first (see | ||
88 | * it's documentation in i2400m.h). | ||
89 | * | ||
90 | * @func: pointer to our SDIO function | ||
91 | * | ||
92 | * @tx_worker: workqueue struct used to TX data when the bus-generic | ||
93 | * code signals packets are pending for transmission to the device. | ||
94 | * | ||
95 | * @tx_workqueue: workqeueue used for data TX; we don't use the | ||
96 | * system's workqueue as that might cause deadlocks with code in | ||
97 | * the bus-generic driver. | ||
98 | */ | ||
99 | struct i2400ms { | ||
100 | struct i2400m i2400m; /* FIRST! See doc */ | ||
101 | struct sdio_func *func; | ||
102 | |||
103 | struct work_struct tx_worker; | ||
104 | struct workqueue_struct *tx_workqueue; | ||
105 | char tx_wq_name[32]; | ||
106 | |||
107 | struct dentry *debugfs_dentry; | ||
108 | }; | ||
109 | |||
110 | |||
111 | static inline | ||
112 | void i2400ms_init(struct i2400ms *i2400ms) | ||
113 | { | ||
114 | i2400m_init(&i2400ms->i2400m); | ||
115 | } | ||
116 | |||
117 | |||
118 | extern int i2400ms_rx_setup(struct i2400ms *); | ||
119 | extern void i2400ms_rx_release(struct i2400ms *); | ||
120 | extern ssize_t __i2400ms_rx_get_size(struct i2400ms *); | ||
121 | |||
122 | extern int i2400ms_tx_setup(struct i2400ms *); | ||
123 | extern void i2400ms_tx_release(struct i2400ms *); | ||
124 | extern void i2400ms_bus_tx_kick(struct i2400m *); | ||
125 | |||
126 | extern ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *, | ||
127 | const struct i2400m_bootrom_header *, | ||
128 | size_t, int); | ||
129 | extern ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *, | ||
130 | struct i2400m_bootrom_header *, | ||
131 | size_t); | ||
132 | #endif /* #ifndef __I2400M_SDIO_H__ */ | ||
diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h new file mode 100644 index 000000000000..6f76558b170f --- /dev/null +++ b/drivers/net/wimax/i2400m/i2400m-usb.h | |||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * USB-specific i2400m driver definitions | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
37 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
38 | * - Initial implementation | ||
39 | * | ||
40 | * | ||
41 | * This driver implements the bus-specific part of the i2400m for | ||
42 | * USB. Check i2400m.h for a generic driver description. | ||
43 | * | ||
44 | * ARCHITECTURE | ||
45 | * | ||
46 | * This driver listens to notifications sent from the notification | ||
47 | * endpoint (in usb-notif.c); when data is ready to read, the code in | ||
48 | * there schedules a read from the device (usb-rx.c) and then passes | ||
49 | * the data to the generic RX code (rx.c). | ||
50 | * | ||
51 | * When the generic driver needs to send data (network or control), it | ||
52 | * queues up in the TX FIFO (tx.c) and that will notify the driver | ||
53 | * through the i2400m->bus_tx_kick() callback | ||
54 | * (usb-tx.c:i2400mu_bus_tx_kick) which will send the items in the | ||
55 | * FIFO queue. | ||
56 | * | ||
57 | * This driver, as well, implements the USB-specific ops for the generic | ||
58 | * driver to be able to setup/teardown communication with the device | ||
59 | * [i2400m_bus_dev_start() and i2400m_bus_dev_stop()], reseting the | ||
60 | * device [i2400m_bus_reset()] and performing firmware upload | ||
61 | * [i2400m_bus_bm_cmd() and i2400_bus_bm_wait_for_ack()]. | ||
62 | */ | ||
63 | |||
64 | #ifndef __I2400M_USB_H__ | ||
65 | #define __I2400M_USB_H__ | ||
66 | |||
67 | #include "i2400m.h" | ||
68 | #include <linux/kthread.h> | ||
69 | |||
70 | |||
71 | /* | ||
72 | * Error Density Count: cheapo error density (over time) counter | ||
73 | * | ||
74 | * Originally by Reinette Chatre <reinette.chatre@intel.com> | ||
75 | * | ||
76 | * Embed an 'struct edc' somewhere. Each time there is a soft or | ||
77 | * retryable error, call edc_inc() and check if the error top | ||
78 | * watermark has been reached. | ||
79 | */ | ||
80 | enum { | ||
81 | EDC_MAX_ERRORS = 10, | ||
82 | EDC_ERROR_TIMEFRAME = HZ, | ||
83 | }; | ||
84 | |||
85 | /* error density counter */ | ||
86 | struct edc { | ||
87 | unsigned long timestart; | ||
88 | u16 errorcount; | ||
89 | }; | ||
90 | |||
91 | static inline void edc_init(struct edc *edc) | ||
92 | { | ||
93 | edc->timestart = jiffies; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * edc_inc - report a soft error and check if we are over the watermark | ||
98 | * | ||
99 | * @edc: pointer to error density counter. | ||
100 | * @max_err: maximum number of errors we can accept over the timeframe | ||
101 | * @timeframe: lenght of the timeframe (in jiffies). | ||
102 | * | ||
103 | * Returns: !0 1 if maximum acceptable errors per timeframe has been | ||
104 | * exceeded. 0 otherwise. | ||
105 | * | ||
106 | * This is way to determine if the number of acceptable errors per time | ||
107 | * period has been exceeded. It is not accurate as there are cases in which | ||
108 | * this scheme will not work, for example if there are periodic occurences | ||
109 | * of errors that straddle updates to the start time. This scheme is | ||
110 | * sufficient for our usage. | ||
111 | * | ||
112 | * To use, embed a 'struct edc' somewhere, initialize it with | ||
113 | * edc_init() and when an error hits: | ||
114 | * | ||
115 | * if (do_something_fails_with_a_soft_error) { | ||
116 | * if (edc_inc(&my->edc, MAX_ERRORS, MAX_TIMEFRAME)) | ||
117 | * Ops, hard error, do something about it | ||
118 | * else | ||
119 | * Retry or ignore, depending on whatever | ||
120 | * } | ||
121 | */ | ||
122 | static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe) | ||
123 | { | ||
124 | unsigned long now; | ||
125 | |||
126 | now = jiffies; | ||
127 | if (now - edc->timestart > timeframe) { | ||
128 | edc->errorcount = 1; | ||
129 | edc->timestart = now; | ||
130 | } else if (++edc->errorcount > max_err) { | ||
131 | edc->errorcount = 0; | ||
132 | edc->timestart = now; | ||
133 | return 1; | ||
134 | } | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | /* Host-Device interface for USB */ | ||
139 | enum { | ||
140 | I2400MU_MAX_NOTIFICATION_LEN = 256, | ||
141 | I2400MU_BLK_SIZE = 16, | ||
142 | I2400MU_PL_SIZE_MAX = 0x3EFF, | ||
143 | |||
144 | /* Endpoints */ | ||
145 | I2400MU_EP_BULK_OUT = 0, | ||
146 | I2400MU_EP_NOTIFICATION, | ||
147 | I2400MU_EP_RESET_COLD, | ||
148 | I2400MU_EP_BULK_IN, | ||
149 | }; | ||
150 | |||
151 | |||
152 | /** | ||
153 | * struct i2400mu - descriptor for a USB connected i2400m | ||
154 | * | ||
155 | * @i2400m: bus-generic i2400m implementation; has to be first (see | ||
156 | * it's documentation in i2400m.h). | ||
157 | * | ||
158 | * @usb_dev: pointer to our USB device | ||
159 | * | ||
160 | * @usb_iface: pointer to our USB interface | ||
161 | * | ||
162 | * @urb_edc: error density counter; used to keep a density-on-time tab | ||
163 | * on how many soft (retryable or ignorable) errors we get. If we | ||
164 | * go over the threshold, we consider the bus transport is failing | ||
165 | * too much and reset. | ||
166 | * | ||
167 | * @notif_urb: URB for receiving notifications from the device. | ||
168 | * | ||
169 | * @tx_kthread: thread we use for data TX. We use a thread because in | ||
170 | * order to do deep power saving and put the device to sleep, we | ||
171 | * need to call usb_autopm_*() [blocking functions]. | ||
172 | * | ||
173 | * @tx_wq: waitqueue for the TX kthread to sleep when there is no data | ||
174 | * to be sent; when more data is available, it is woken up by | ||
175 | * i2400mu_bus_tx_kick(). | ||
176 | * | ||
177 | * @rx_kthread: thread we use for data RX. We use a thread because in | ||
178 | * order to do deep power saving and put the device to sleep, we | ||
179 | * need to call usb_autopm_*() [blocking functions]. | ||
180 | * | ||
181 | * @rx_wq: waitqueue for the RX kthread to sleep when there is no data | ||
182 | * to receive. When data is available, it is woken up by | ||
183 | * usb-notif.c:i2400mu_notification_grok(). | ||
184 | * | ||
185 | * @rx_pending_count: number of rx-data-ready notifications that were | ||
186 | * still not handled by the RX kthread. | ||
187 | * | ||
188 | * @rx_size: current RX buffer size that is being used. | ||
189 | * | ||
190 | * @rx_size_acc: accumulator of the sizes of the previous read | ||
191 | * transactions. | ||
192 | * | ||
193 | * @rx_size_cnt: number of read transactions accumulated in | ||
194 | * @rx_size_acc. | ||
195 | * | ||
196 | * @do_autopm: disable(0)/enable(>0) calling the | ||
197 | * usb_autopm_get/put_interface() barriers when executing | ||
198 | * commands. See doc in i2400mu_suspend() for more information. | ||
199 | * | ||
200 | * @rx_size_auto_shrink: if true, the rx_size is shrinked | ||
201 | * automatically based on the average size of the received | ||
202 | * transactions. This allows the receive code to allocate smaller | ||
203 | * chunks of memory and thus reduce pressure on the memory | ||
204 | * allocator by not wasting so much space. By default it is | ||
205 | * enabled. | ||
206 | * | ||
207 | * @debugfs_dentry: hookup for debugfs files. | ||
208 | * These have to be in a separate directory, a child of | ||
209 | * (wimax_dev->debugfs_dentry) so they can be removed when the | ||
210 | * module unloads, as we don't keep each dentry. | ||
211 | */ | ||
212 | struct i2400mu { | ||
213 | struct i2400m i2400m; /* FIRST! See doc */ | ||
214 | |||
215 | struct usb_device *usb_dev; | ||
216 | struct usb_interface *usb_iface; | ||
217 | struct edc urb_edc; /* Error density counter */ | ||
218 | |||
219 | struct urb *notif_urb; | ||
220 | struct task_struct *tx_kthread; | ||
221 | wait_queue_head_t tx_wq; | ||
222 | |||
223 | struct task_struct *rx_kthread; | ||
224 | wait_queue_head_t rx_wq; | ||
225 | atomic_t rx_pending_count; | ||
226 | size_t rx_size, rx_size_acc, rx_size_cnt; | ||
227 | atomic_t do_autopm; | ||
228 | u8 rx_size_auto_shrink; | ||
229 | |||
230 | struct dentry *debugfs_dentry; | ||
231 | }; | ||
232 | |||
233 | |||
234 | static inline | ||
235 | void i2400mu_init(struct i2400mu *i2400mu) | ||
236 | { | ||
237 | i2400m_init(&i2400mu->i2400m); | ||
238 | edc_init(&i2400mu->urb_edc); | ||
239 | init_waitqueue_head(&i2400mu->tx_wq); | ||
240 | atomic_set(&i2400mu->rx_pending_count, 0); | ||
241 | init_waitqueue_head(&i2400mu->rx_wq); | ||
242 | i2400mu->rx_size = PAGE_SIZE - sizeof(struct skb_shared_info); | ||
243 | atomic_set(&i2400mu->do_autopm, 1); | ||
244 | i2400mu->rx_size_auto_shrink = 1; | ||
245 | } | ||
246 | |||
247 | extern int i2400mu_notification_setup(struct i2400mu *); | ||
248 | extern void i2400mu_notification_release(struct i2400mu *); | ||
249 | |||
250 | extern int i2400mu_rx_setup(struct i2400mu *); | ||
251 | extern void i2400mu_rx_release(struct i2400mu *); | ||
252 | extern void i2400mu_rx_kick(struct i2400mu *); | ||
253 | |||
254 | extern int i2400mu_tx_setup(struct i2400mu *); | ||
255 | extern void i2400mu_tx_release(struct i2400mu *); | ||
256 | extern void i2400mu_bus_tx_kick(struct i2400m *); | ||
257 | |||
258 | extern ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *, | ||
259 | const struct i2400m_bootrom_header *, | ||
260 | size_t, int); | ||
261 | extern ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *, | ||
262 | struct i2400m_bootrom_header *, | ||
263 | size_t); | ||
264 | #endif /* #ifndef __I2400M_USB_H__ */ | ||
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h new file mode 100644 index 000000000000..067c871cc226 --- /dev/null +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -0,0 +1,755 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Declarations for bus-generic internal APIs | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
37 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
38 | * - Initial implementation | ||
39 | * | ||
40 | * | ||
41 | * GENERAL DRIVER ARCHITECTURE | ||
42 | * | ||
43 | * The i2400m driver is split in the following two major parts: | ||
44 | * | ||
45 | * - bus specific driver | ||
46 | * - bus generic driver (this part) | ||
47 | * | ||
48 | * The bus specific driver sets up stuff specific to the bus the | ||
49 | * device is connected to (USB, SDIO, PCI, tam-tam...non-authoritative | ||
50 | * nor binding list) which is basically the device-model management | ||
51 | * (probe/disconnect, etc), moving data from device to kernel and | ||
52 | * back, doing the power saving details and reseting the device. | ||
53 | * | ||
54 | * For details on each bus-specific driver, see it's include file, | ||
55 | * i2400m-BUSNAME.h | ||
56 | * | ||
57 | * The bus-generic functionality break up is: | ||
58 | * | ||
59 | * - Firmware upload: fw.c - takes care of uploading firmware to the | ||
60 | * device. bus-specific driver just needs to provides a way to | ||
61 | * execute boot-mode commands and to reset the device. | ||
62 | * | ||
63 | * - RX handling: rx.c - receives data from the bus-specific code and | ||
64 | * feeds it to the network or WiMAX stack or uses it to modify | ||
65 | * the driver state. bus-specific driver only has to receive | ||
66 | * frames and pass them to this module. | ||
67 | * | ||
68 | * - TX handling: tx.c - manages the TX FIFO queue and provides means | ||
69 | * for the bus-specific TX code to pull data from the FIFO | ||
70 | * queue. bus-specific code just pulls frames from this module | ||
71 | * to sends them to the device. | ||
72 | * | ||
73 | * - netdev glue: netdev.c - interface with Linux networking | ||
74 | * stack. Pass around data frames, and configure when the | ||
75 | * device is up and running or shutdown (through ifconfig up / | ||
76 | * down). Bus-generic only. | ||
77 | * | ||
78 | * - control ops: control.c - implements various commmands for | ||
79 | * controlling the device. bus-generic only. | ||
80 | * | ||
81 | * - device model glue: driver.c - implements helpers for the | ||
82 | * device-model glue done by the bus-specific layer | ||
83 | * (setup/release the driver resources), turning the device on | ||
84 | * and off, handling the device reboots/resets and a few simple | ||
85 | * WiMAX stack ops. | ||
86 | * | ||
87 | * Code is also broken up in linux-glue / device-glue. | ||
88 | * | ||
89 | * Linux glue contains functions that deal mostly with gluing with the | ||
90 | * rest of the Linux kernel. | ||
91 | * | ||
92 | * Device-glue are functions that deal mostly with the way the device | ||
93 | * does things and talk the device's language. | ||
94 | * | ||
95 | * device-glue code is licensed BSD so other open source OSes can take | ||
96 | * it to implement their drivers. | ||
97 | * | ||
98 | * | ||
99 | * APIs AND HEADER FILES | ||
100 | * | ||
101 | * This bus generic code exports three APIs: | ||
102 | * | ||
103 | * - HDI (host-device interface) definitions common to all busses | ||
104 | * (include/linux/wimax/i2400m.h); these can be also used by user | ||
105 | * space code. | ||
106 | * - internal API for the bus-generic code | ||
107 | * - external API for the bus-specific drivers | ||
108 | * | ||
109 | * | ||
110 | * LIFE CYCLE: | ||
111 | * | ||
112 | * When the bus-specific driver probes, it allocates a network device | ||
113 | * with enough space for it's data structue, that must contain a | ||
114 | * &struct i2400m at the top. | ||
115 | * | ||
116 | * On probe, it needs to fill the i2400m members marked as [fill], as | ||
117 | * well as i2400m->wimax_dev.net_dev and call i2400m_setup(). The | ||
118 | * i2400m driver will only register with the WiMAX and network stacks; | ||
119 | * the only access done to the device is to read the MAC address so we | ||
120 | * can register a network device. This calls i2400m_dev_start() to | ||
121 | * load firmware, setup communication with the device and configure it | ||
122 | * for operation. | ||
123 | * | ||
124 | * At this point, control and data communications are possible. | ||
125 | * | ||
126 | * On disconnect/driver unload, the bus-specific disconnect function | ||
127 | * calls i2400m_release() to undo i2400m_setup(). i2400m_dev_stop() | ||
128 | * shuts the firmware down and releases resources uses to communicate | ||
129 | * with the device. | ||
130 | * | ||
131 | * While the device is up, it might reset. The bus-specific driver has | ||
132 | * to catch that situation and call i2400m_dev_reset_handle() to deal | ||
133 | * with it (reset the internal driver structures and go back to square | ||
134 | * one). | ||
135 | */ | ||
136 | |||
137 | #ifndef __I2400M_H__ | ||
138 | #define __I2400M_H__ | ||
139 | |||
140 | #include <linux/usb.h> | ||
141 | #include <linux/netdevice.h> | ||
142 | #include <linux/completion.h> | ||
143 | #include <linux/rwsem.h> | ||
144 | #include <asm/atomic.h> | ||
145 | #include <net/wimax.h> | ||
146 | #include <linux/wimax/i2400m.h> | ||
147 | #include <asm/byteorder.h> | ||
148 | |||
149 | /* Misc constants */ | ||
150 | enum { | ||
151 | /* Firmware uploading */ | ||
152 | I2400M_BOOT_RETRIES = 3, | ||
153 | /* Size of the Boot Mode Command buffer */ | ||
154 | I2400M_BM_CMD_BUF_SIZE = 16 * 1024, | ||
155 | I2400M_BM_ACK_BUF_SIZE = 256, | ||
156 | }; | ||
157 | |||
158 | |||
159 | /* Firmware version we request when pulling the fw image file */ | ||
160 | #define I2400M_FW_VERSION "1.3" | ||
161 | |||
162 | |||
163 | /** | ||
164 | * i2400m_reset_type - methods to reset a device | ||
165 | * | ||
166 | * @I2400M_RT_WARM: Reset without device disconnection, device handles | ||
167 | * are kept valid but state is back to power on, with firmware | ||
168 | * re-uploaded. | ||
169 | * @I2400M_RT_COLD: Tell the device to disconnect itself from the bus | ||
170 | * and reconnect. Renders all device handles invalid. | ||
171 | * @I2400M_RT_BUS: Tells the bus to reset the device; last measure | ||
172 | * used when both types above don't work. | ||
173 | */ | ||
174 | enum i2400m_reset_type { | ||
175 | I2400M_RT_WARM, /* first measure */ | ||
176 | I2400M_RT_COLD, /* second measure */ | ||
177 | I2400M_RT_BUS, /* call in artillery */ | ||
178 | }; | ||
179 | |||
180 | struct i2400m_reset_ctx; | ||
181 | |||
182 | /** | ||
183 | * struct i2400m - descriptor for an Intel 2400m | ||
184 | * | ||
185 | * Members marked with [fill] must be filled out/initialized before | ||
186 | * calling i2400m_setup(). | ||
187 | * | ||
188 | * @bus_tx_block_size: [fill] SDIO imposes a 256 block size, USB 16, | ||
189 | * so we have a tx_blk_size variable that the bus layer sets to | ||
190 | * tell the engine how much of that we need. | ||
191 | * | ||
192 | * @bus_pl_size_max: [fill] Maximum payload size. | ||
193 | * | ||
194 | * @bus_dev_start: [fill] Function called by the bus-generic code | ||
195 | * [i2400m_dev_start()] to setup the bus-specific communications | ||
196 | * to the the device. See LIFE CYCLE above. | ||
197 | * | ||
198 | * NOTE: Doesn't need to upload the firmware, as that is taken | ||
199 | * care of by the bus-generic code. | ||
200 | * | ||
201 | * @bus_dev_stop: [fill] Function called by the bus-generic code | ||
202 | * [i2400m_dev_stop()] to shutdown the bus-specific communications | ||
203 | * to the the device. See LIFE CYCLE above. | ||
204 | * | ||
205 | * This function does not need to reset the device, just tear down | ||
206 | * all the host resources created to handle communication with | ||
207 | * the device. | ||
208 | * | ||
209 | * @bus_tx_kick: [fill] Function called by the bus-generic code to let | ||
210 | * the bus-specific code know that there is data available in the | ||
211 | * TX FIFO for transmission to the device. | ||
212 | * | ||
213 | * This function cannot sleep. | ||
214 | * | ||
215 | * @bus_reset: [fill] Function called by the bus-generic code to reset | ||
216 | * the device in in various ways. Doesn't need to wait for the | ||
217 | * reset to finish. | ||
218 | * | ||
219 | * If warm or cold reset fail, this function is expected to do a | ||
220 | * bus-specific reset (eg: USB reset) to get the device to a | ||
221 | * working state (even if it implies device disconecction). | ||
222 | * | ||
223 | * Note the warm reset is used by the firmware uploader to | ||
224 | * reinitialize the device. | ||
225 | * | ||
226 | * IMPORTANT: this is called very early in the device setup | ||
227 | * process, so it cannot rely on common infrastructure being laid | ||
228 | * out. | ||
229 | * | ||
230 | * @bus_bm_cmd_send: [fill] Function called to send a boot-mode | ||
231 | * command. Flags are defined in 'enum i2400m_bm_cmd_flags'. This | ||
232 | * is synchronous and has to return 0 if ok or < 0 errno code in | ||
233 | * any error condition. | ||
234 | * | ||
235 | * @bus_bm_wait_for_ack: [fill] Function called to wait for a | ||
236 | * boot-mode notification (that can be a response to a previously | ||
237 | * issued command or an asynchronous one). Will read until all the | ||
238 | * indicated size is read or timeout. Reading more or less data | ||
239 | * than asked for is an error condition. Return 0 if ok, < 0 errno | ||
240 | * code on error. | ||
241 | * | ||
242 | * The caller to this function will check if the response is a | ||
243 | * barker that indicates the device going into reset mode. | ||
244 | * | ||
245 | * @bus_fw_name: [fill] name of the firmware image (in most cases, | ||
246 | * they are all the same for a single release, except that they | ||
247 | * have the type of the bus embedded in the name (eg: | ||
248 | * i2400m-fw-X-VERSION.sbcf, where X is the bus name). | ||
249 | * | ||
250 | * @bus_bm_mac_addr_impaired: [fill] Set to true if the device's MAC | ||
251 | * address provided in boot mode is kind of broken and needs to | ||
252 | * be re-read later on. | ||
253 | * | ||
254 | * | ||
255 | * @wimax_dev: WiMAX generic device for linkage into the kernel WiMAX | ||
256 | * stack. Due to the way a net_device is allocated, we need to | ||
257 | * force this to be the first field so that we can get from | ||
258 | * netdev_priv() the right pointer. | ||
259 | * | ||
260 | * @state: device's state (as reported by it) | ||
261 | * | ||
262 | * @state_wq: waitqueue that is woken up whenever the state changes | ||
263 | * | ||
264 | * @tx_lock: spinlock to protect TX members | ||
265 | * | ||
266 | * @tx_buf: FIFO buffer for TX; we queue data here | ||
267 | * | ||
268 | * @tx_in: FIFO index for incoming data. Note this doesn't wrap around | ||
269 | * and it is always greater than @tx_out. | ||
270 | * | ||
271 | * @tx_out: FIFO index for outgoing data | ||
272 | * | ||
273 | * @tx_msg: current TX message that is active in the FIFO for | ||
274 | * appending payloads. | ||
275 | * | ||
276 | * @tx_sequence: current sequence number for TX messages from the | ||
277 | * device to the host. | ||
278 | * | ||
279 | * @tx_msg_size: size of the current message being transmitted by the | ||
280 | * bus-specific code. | ||
281 | * | ||
282 | * @tx_pl_num: total number of payloads sent | ||
283 | * | ||
284 | * @tx_pl_max: maximum number of payloads sent in a TX message | ||
285 | * | ||
286 | * @tx_pl_min: minimum number of payloads sent in a TX message | ||
287 | * | ||
288 | * @tx_num: number of TX messages sent | ||
289 | * | ||
290 | * @tx_size_acc: number of bytes in all TX messages sent | ||
291 | * (this is different to net_dev's statistics as it also counts | ||
292 | * control messages). | ||
293 | * | ||
294 | * @tx_size_min: smallest TX message sent. | ||
295 | * | ||
296 | * @tx_size_max: biggest TX message sent. | ||
297 | * | ||
298 | * @rx_lock: spinlock to protect RX members | ||
299 | * | ||
300 | * @rx_pl_num: total number of payloads received | ||
301 | * | ||
302 | * @rx_pl_max: maximum number of payloads received in a RX message | ||
303 | * | ||
304 | * @rx_pl_min: minimum number of payloads received in a RX message | ||
305 | * | ||
306 | * @rx_num: number of RX messages received | ||
307 | * | ||
308 | * @rx_size_acc: number of bytes in all RX messages received | ||
309 | * (this is different to net_dev's statistics as it also counts | ||
310 | * control messages). | ||
311 | * | ||
312 | * @rx_size_min: smallest RX message received. | ||
313 | * | ||
314 | * @rx_size_max: buggest RX message received. | ||
315 | * | ||
316 | * @init_mutex: Mutex used for serializing the device bringup | ||
317 | * sequence; this way if the device reboots in the middle, we | ||
318 | * don't try to do a bringup again while we are tearing down the | ||
319 | * one that failed. | ||
320 | * | ||
321 | * Can't reuse @msg_mutex because from within the bringup sequence | ||
322 | * we need to send messages to the device and thus use @msg_mutex. | ||
323 | * | ||
324 | * @msg_mutex: mutex used to send control commands to the device (we | ||
325 | * only allow one at a time, per host-device interface design). | ||
326 | * | ||
327 | * @msg_completion: used to wait for an ack to a control command sent | ||
328 | * to the device. | ||
329 | * | ||
330 | * @ack_skb: used to store the actual ack to a control command if the | ||
331 | * reception of the command was successful. Otherwise, a ERR_PTR() | ||
332 | * errno code that indicates what failed with the ack reception. | ||
333 | * | ||
334 | * Only valid after @msg_completion is woken up. Only updateable | ||
335 | * if @msg_completion is armed. Only touched by | ||
336 | * i2400m_msg_to_dev(). | ||
337 | * | ||
338 | * Protected by @rx_lock. In theory the command execution flow is | ||
339 | * sequential, but in case the device sends an out-of-phase or | ||
340 | * very delayed response, we need to avoid it trampling current | ||
341 | * execution. | ||
342 | * | ||
343 | * @bm_cmd_buf: boot mode command buffer for composing firmware upload | ||
344 | * commands. | ||
345 | * | ||
346 | * USB can't r/w to stack, vmalloc, etc...as well, we end up | ||
347 | * having to alloc/free a lot to compose commands, so we use these | ||
348 | * for stagging and not having to realloc all the time. | ||
349 | * | ||
350 | * This assumes the code always runs serialized. Only one thread | ||
351 | * can call i2400m_bm_cmd() at the same time. | ||
352 | * | ||
353 | * @bm_ack_buf: boot mode acknoledge buffer for staging reception of | ||
354 | * responses to commands. | ||
355 | * | ||
356 | * See @bm_cmd_buf. | ||
357 | * | ||
358 | * @work_queue: work queue for processing device reports. This | ||
359 | * workqueue cannot be used for processing TX or RX to the device, | ||
360 | * as from it we'll process device reports, which might require | ||
361 | * further communication with the device. | ||
362 | * | ||
363 | * @debugfs_dentry: hookup for debugfs files. | ||
364 | * These have to be in a separate directory, a child of | ||
365 | * (wimax_dev->debugfs_dentry) so they can be removed when the | ||
366 | * module unloads, as we don't keep each dentry. | ||
367 | */ | ||
368 | struct i2400m { | ||
369 | struct wimax_dev wimax_dev; /* FIRST! See doc */ | ||
370 | |||
371 | unsigned updown:1; /* Network device is up or down */ | ||
372 | unsigned boot_mode:1; /* is the device in boot mode? */ | ||
373 | unsigned sboot:1; /* signed or unsigned fw boot */ | ||
374 | unsigned ready:1; /* all probing steps done */ | ||
375 | u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ | ||
376 | /* typed u8 so debugfs/u8 can tweak */ | ||
377 | enum i2400m_system_state state; | ||
378 | wait_queue_head_t state_wq; /* Woken up when on state updates */ | ||
379 | |||
380 | size_t bus_tx_block_size; | ||
381 | size_t bus_pl_size_max; | ||
382 | int (*bus_dev_start)(struct i2400m *); | ||
383 | void (*bus_dev_stop)(struct i2400m *); | ||
384 | void (*bus_tx_kick)(struct i2400m *); | ||
385 | int (*bus_reset)(struct i2400m *, enum i2400m_reset_type); | ||
386 | ssize_t (*bus_bm_cmd_send)(struct i2400m *, | ||
387 | const struct i2400m_bootrom_header *, | ||
388 | size_t, int flags); | ||
389 | ssize_t (*bus_bm_wait_for_ack)(struct i2400m *, | ||
390 | struct i2400m_bootrom_header *, size_t); | ||
391 | const char *bus_fw_name; | ||
392 | unsigned bus_bm_mac_addr_impaired:1; | ||
393 | |||
394 | spinlock_t tx_lock; /* protect TX state */ | ||
395 | void *tx_buf; | ||
396 | size_t tx_in, tx_out; | ||
397 | struct i2400m_msg_hdr *tx_msg; | ||
398 | size_t tx_sequence, tx_msg_size; | ||
399 | /* TX stats */ | ||
400 | unsigned tx_pl_num, tx_pl_max, tx_pl_min, | ||
401 | tx_num, tx_size_acc, tx_size_min, tx_size_max; | ||
402 | |||
403 | /* RX stats */ | ||
404 | spinlock_t rx_lock; /* protect RX state */ | ||
405 | unsigned rx_pl_num, rx_pl_max, rx_pl_min, | ||
406 | rx_num, rx_size_acc, rx_size_min, rx_size_max; | ||
407 | |||
408 | struct mutex msg_mutex; /* serialize command execution */ | ||
409 | struct completion msg_completion; | ||
410 | struct sk_buff *ack_skb; /* protected by rx_lock */ | ||
411 | |||
412 | void *bm_ack_buf; /* for receiving acks over USB */ | ||
413 | void *bm_cmd_buf; /* for issuing commands over USB */ | ||
414 | |||
415 | struct workqueue_struct *work_queue; | ||
416 | |||
417 | struct mutex init_mutex; /* protect bringup seq */ | ||
418 | struct i2400m_reset_ctx *reset_ctx; /* protected by init_mutex */ | ||
419 | |||
420 | struct work_struct wake_tx_ws; | ||
421 | struct sk_buff *wake_tx_skb; | ||
422 | |||
423 | struct dentry *debugfs_dentry; | ||
424 | }; | ||
425 | |||
426 | |||
427 | /* | ||
428 | * Initialize a 'struct i2400m' from all zeroes | ||
429 | * | ||
430 | * This is a bus-generic API call. | ||
431 | */ | ||
432 | static inline | ||
433 | void i2400m_init(struct i2400m *i2400m) | ||
434 | { | ||
435 | wimax_dev_init(&i2400m->wimax_dev); | ||
436 | |||
437 | i2400m->boot_mode = 1; | ||
438 | init_waitqueue_head(&i2400m->state_wq); | ||
439 | |||
440 | spin_lock_init(&i2400m->tx_lock); | ||
441 | i2400m->tx_pl_min = UINT_MAX; | ||
442 | i2400m->tx_size_min = UINT_MAX; | ||
443 | |||
444 | spin_lock_init(&i2400m->rx_lock); | ||
445 | i2400m->rx_pl_min = UINT_MAX; | ||
446 | i2400m->rx_size_min = UINT_MAX; | ||
447 | |||
448 | mutex_init(&i2400m->msg_mutex); | ||
449 | init_completion(&i2400m->msg_completion); | ||
450 | |||
451 | mutex_init(&i2400m->init_mutex); | ||
452 | /* wake_tx_ws is initialized in i2400m_tx_setup() */ | ||
453 | } | ||
454 | |||
455 | |||
456 | /* | ||
457 | * Bus-generic internal APIs | ||
458 | * ------------------------- | ||
459 | */ | ||
460 | |||
461 | static inline | ||
462 | struct i2400m *wimax_dev_to_i2400m(struct wimax_dev *wimax_dev) | ||
463 | { | ||
464 | return container_of(wimax_dev, struct i2400m, wimax_dev); | ||
465 | } | ||
466 | |||
467 | static inline | ||
468 | struct i2400m *net_dev_to_i2400m(struct net_device *net_dev) | ||
469 | { | ||
470 | return wimax_dev_to_i2400m(netdev_priv(net_dev)); | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * Boot mode support | ||
475 | */ | ||
476 | |||
477 | /** | ||
478 | * i2400m_bm_cmd_flags - flags to i2400m_bm_cmd() | ||
479 | * | ||
480 | * @I2400M_BM_CMD_RAW: send the command block as-is, without doing any | ||
481 | * extra processing for adding CRC. | ||
482 | */ | ||
483 | enum i2400m_bm_cmd_flags { | ||
484 | I2400M_BM_CMD_RAW = 1 << 2, | ||
485 | }; | ||
486 | |||
487 | /** | ||
488 | * i2400m_bri - Boot-ROM indicators | ||
489 | * | ||
490 | * Flags for i2400m_bootrom_init() and i2400m_dev_bootstrap() [which | ||
491 | * are passed from things like i2400m_setup()]. Can be combined with | ||
492 | * |. | ||
493 | * | ||
494 | * @I2400M_BRI_SOFT: The device rebooted already and a reboot | ||
495 | * barker received, proceed directly to ack the boot sequence. | ||
496 | * @I2400M_BRI_NO_REBOOT: Do not reboot the device and proceed | ||
497 | * directly to wait for a reboot barker from the device. | ||
498 | * @I2400M_BRI_MAC_REINIT: We need to reinitialize the boot | ||
499 | * rom after reading the MAC adress. This is quite a dirty hack, | ||
500 | * if you ask me -- the device requires the bootrom to be | ||
501 | * intialized after reading the MAC address. | ||
502 | */ | ||
503 | enum i2400m_bri { | ||
504 | I2400M_BRI_SOFT = 1 << 1, | ||
505 | I2400M_BRI_NO_REBOOT = 1 << 2, | ||
506 | I2400M_BRI_MAC_REINIT = 1 << 3, | ||
507 | }; | ||
508 | |||
509 | extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *); | ||
510 | extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri); | ||
511 | extern int i2400m_read_mac_addr(struct i2400m *); | ||
512 | extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri); | ||
513 | |||
514 | /* Make/grok boot-rom header commands */ | ||
515 | |||
516 | static inline | ||
517 | __le32 i2400m_brh_command(enum i2400m_brh_opcode opcode, unsigned use_checksum, | ||
518 | unsigned direct_access) | ||
519 | { | ||
520 | return cpu_to_le32( | ||
521 | I2400M_BRH_SIGNATURE | ||
522 | | (direct_access ? I2400M_BRH_DIRECT_ACCESS : 0) | ||
523 | | I2400M_BRH_RESPONSE_REQUIRED /* response always required */ | ||
524 | | (use_checksum ? I2400M_BRH_USE_CHECKSUM : 0) | ||
525 | | (opcode & I2400M_BRH_OPCODE_MASK)); | ||
526 | } | ||
527 | |||
528 | static inline | ||
529 | void i2400m_brh_set_opcode(struct i2400m_bootrom_header *hdr, | ||
530 | enum i2400m_brh_opcode opcode) | ||
531 | { | ||
532 | hdr->command = cpu_to_le32( | ||
533 | (le32_to_cpu(hdr->command) & ~I2400M_BRH_OPCODE_MASK) | ||
534 | | (opcode & I2400M_BRH_OPCODE_MASK)); | ||
535 | } | ||
536 | |||
537 | static inline | ||
538 | unsigned i2400m_brh_get_opcode(const struct i2400m_bootrom_header *hdr) | ||
539 | { | ||
540 | return le32_to_cpu(hdr->command) & I2400M_BRH_OPCODE_MASK; | ||
541 | } | ||
542 | |||
543 | static inline | ||
544 | unsigned i2400m_brh_get_response(const struct i2400m_bootrom_header *hdr) | ||
545 | { | ||
546 | return (le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_MASK) | ||
547 | >> I2400M_BRH_RESPONSE_SHIFT; | ||
548 | } | ||
549 | |||
550 | static inline | ||
551 | unsigned i2400m_brh_get_use_checksum(const struct i2400m_bootrom_header *hdr) | ||
552 | { | ||
553 | return le32_to_cpu(hdr->command) & I2400M_BRH_USE_CHECKSUM; | ||
554 | } | ||
555 | |||
556 | static inline | ||
557 | unsigned i2400m_brh_get_response_required( | ||
558 | const struct i2400m_bootrom_header *hdr) | ||
559 | { | ||
560 | return le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_REQUIRED; | ||
561 | } | ||
562 | |||
563 | static inline | ||
564 | unsigned i2400m_brh_get_direct_access(const struct i2400m_bootrom_header *hdr) | ||
565 | { | ||
566 | return le32_to_cpu(hdr->command) & I2400M_BRH_DIRECT_ACCESS; | ||
567 | } | ||
568 | |||
569 | static inline | ||
570 | unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr) | ||
571 | { | ||
572 | return (le32_to_cpu(hdr->command) & I2400M_BRH_SIGNATURE_MASK) | ||
573 | >> I2400M_BRH_SIGNATURE_SHIFT; | ||
574 | } | ||
575 | |||
576 | |||
577 | /* | ||
578 | * Driver / device setup and internal functions | ||
579 | */ | ||
580 | extern void i2400m_netdev_setup(struct net_device *net_dev); | ||
581 | extern int i2400m_tx_setup(struct i2400m *); | ||
582 | extern void i2400m_wake_tx_work(struct work_struct *); | ||
583 | extern void i2400m_tx_release(struct i2400m *); | ||
584 | |||
585 | extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, | ||
586 | const void *, int); | ||
587 | enum i2400m_pt; | ||
588 | extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); | ||
589 | |||
590 | #ifdef CONFIG_DEBUG_FS | ||
591 | extern int i2400m_debugfs_add(struct i2400m *); | ||
592 | extern void i2400m_debugfs_rm(struct i2400m *); | ||
593 | #else | ||
594 | static inline int i2400m_debugfs_add(struct i2400m *i2400m) | ||
595 | { | ||
596 | return 0; | ||
597 | } | ||
598 | static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {} | ||
599 | #endif | ||
600 | |||
601 | /* Called by _dev_start()/_dev_stop() to initialize the device itself */ | ||
602 | extern int i2400m_dev_initialize(struct i2400m *); | ||
603 | extern void i2400m_dev_shutdown(struct i2400m *); | ||
604 | |||
605 | extern struct attribute_group i2400m_dev_attr_group; | ||
606 | |||
607 | extern int i2400m_schedule_work(struct i2400m *, | ||
608 | void (*)(struct work_struct *), gfp_t); | ||
609 | |||
610 | /* HDI message's payload description handling */ | ||
611 | |||
612 | static inline | ||
613 | size_t i2400m_pld_size(const struct i2400m_pld *pld) | ||
614 | { | ||
615 | return I2400M_PLD_SIZE_MASK & le32_to_cpu(pld->val); | ||
616 | } | ||
617 | |||
618 | static inline | ||
619 | enum i2400m_pt i2400m_pld_type(const struct i2400m_pld *pld) | ||
620 | { | ||
621 | return (I2400M_PLD_TYPE_MASK & le32_to_cpu(pld->val)) | ||
622 | >> I2400M_PLD_TYPE_SHIFT; | ||
623 | } | ||
624 | |||
625 | static inline | ||
626 | void i2400m_pld_set(struct i2400m_pld *pld, size_t size, | ||
627 | enum i2400m_pt type) | ||
628 | { | ||
629 | pld->val = cpu_to_le32( | ||
630 | ((type << I2400M_PLD_TYPE_SHIFT) & I2400M_PLD_TYPE_MASK) | ||
631 | | (size & I2400M_PLD_SIZE_MASK)); | ||
632 | } | ||
633 | |||
634 | |||
635 | /* | ||
636 | * API for the bus-specific drivers | ||
637 | * -------------------------------- | ||
638 | */ | ||
639 | |||
640 | static inline | ||
641 | struct i2400m *i2400m_get(struct i2400m *i2400m) | ||
642 | { | ||
643 | dev_hold(i2400m->wimax_dev.net_dev); | ||
644 | return i2400m; | ||
645 | } | ||
646 | |||
647 | static inline | ||
648 | void i2400m_put(struct i2400m *i2400m) | ||
649 | { | ||
650 | dev_put(i2400m->wimax_dev.net_dev); | ||
651 | } | ||
652 | |||
653 | extern int i2400m_dev_reset_handle(struct i2400m *); | ||
654 | |||
655 | /* | ||
656 | * _setup()/_release() are called by the probe/disconnect functions of | ||
657 | * the bus-specific drivers. | ||
658 | */ | ||
659 | extern int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags); | ||
660 | extern void i2400m_release(struct i2400m *); | ||
661 | |||
662 | extern int i2400m_rx(struct i2400m *, struct sk_buff *); | ||
663 | extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *); | ||
664 | extern void i2400m_tx_msg_sent(struct i2400m *); | ||
665 | |||
666 | static const __le32 i2400m_NBOOT_BARKER[4] = { | ||
667 | __constant_cpu_to_le32(I2400M_NBOOT_BARKER), | ||
668 | __constant_cpu_to_le32(I2400M_NBOOT_BARKER), | ||
669 | __constant_cpu_to_le32(I2400M_NBOOT_BARKER), | ||
670 | __constant_cpu_to_le32(I2400M_NBOOT_BARKER) | ||
671 | }; | ||
672 | |||
673 | static const __le32 i2400m_SBOOT_BARKER[4] = { | ||
674 | __constant_cpu_to_le32(I2400M_SBOOT_BARKER), | ||
675 | __constant_cpu_to_le32(I2400M_SBOOT_BARKER), | ||
676 | __constant_cpu_to_le32(I2400M_SBOOT_BARKER), | ||
677 | __constant_cpu_to_le32(I2400M_SBOOT_BARKER) | ||
678 | }; | ||
679 | |||
680 | |||
681 | /* | ||
682 | * Utility functions | ||
683 | */ | ||
684 | |||
685 | static inline | ||
686 | struct device *i2400m_dev(struct i2400m *i2400m) | ||
687 | { | ||
688 | return i2400m->wimax_dev.net_dev->dev.parent; | ||
689 | } | ||
690 | |||
691 | /* | ||
692 | * Helper for scheduling simple work functions | ||
693 | * | ||
694 | * This struct can get any kind of payload attached (normally in the | ||
695 | * form of a struct where you pack the stuff you want to pass to the | ||
696 | * _work function). | ||
697 | */ | ||
698 | struct i2400m_work { | ||
699 | struct work_struct ws; | ||
700 | struct i2400m *i2400m; | ||
701 | u8 pl[0]; | ||
702 | }; | ||
703 | extern int i2400m_queue_work(struct i2400m *, | ||
704 | void (*)(struct work_struct *), gfp_t, | ||
705 | const void *, size_t); | ||
706 | |||
707 | extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *, | ||
708 | char *, size_t); | ||
709 | extern int i2400m_msg_size_check(struct i2400m *, | ||
710 | const struct i2400m_l3l4_hdr *, size_t); | ||
711 | extern struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t); | ||
712 | extern void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int); | ||
713 | extern void i2400m_msg_ack_hook(struct i2400m *, | ||
714 | const struct i2400m_l3l4_hdr *, size_t); | ||
715 | extern void i2400m_report_hook(struct i2400m *, | ||
716 | const struct i2400m_l3l4_hdr *, size_t); | ||
717 | extern int i2400m_cmd_enter_powersave(struct i2400m *); | ||
718 | extern int i2400m_cmd_get_state(struct i2400m *); | ||
719 | extern int i2400m_cmd_exit_idle(struct i2400m *); | ||
720 | extern struct sk_buff *i2400m_get_device_info(struct i2400m *); | ||
721 | extern int i2400m_firmware_check(struct i2400m *); | ||
722 | extern int i2400m_set_init_config(struct i2400m *, | ||
723 | const struct i2400m_tlv_hdr **, size_t); | ||
724 | |||
725 | static inline | ||
726 | struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep) | ||
727 | { | ||
728 | return &iface->cur_altsetting->endpoint[ep].desc; | ||
729 | } | ||
730 | |||
731 | extern int i2400m_op_rfkill_sw_toggle(struct wimax_dev *, | ||
732 | enum wimax_rf_state); | ||
733 | extern void i2400m_report_tlv_rf_switches_status( | ||
734 | struct i2400m *, const struct i2400m_tlv_rf_switches_status *); | ||
735 | |||
736 | |||
737 | /* | ||
738 | * Do a millisecond-sleep for allowing wireshark to dump all the data | ||
739 | * packets. Used only for debugging. | ||
740 | */ | ||
741 | static inline | ||
742 | void __i2400m_msleep(unsigned ms) | ||
743 | { | ||
744 | #if 1 | ||
745 | #else | ||
746 | msleep(ms); | ||
747 | #endif | ||
748 | } | ||
749 | |||
750 | /* Module parameters */ | ||
751 | |||
752 | extern int i2400m_idle_mode_disabled; | ||
753 | |||
754 | |||
755 | #endif /* #ifndef __I2400M_H__ */ | ||
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c new file mode 100644 index 000000000000..63fe708e8a31 --- /dev/null +++ b/drivers/net/wimax/i2400m/netdev.c | |||
@@ -0,0 +1,524 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Glue with the networking stack | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
8 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License version | ||
12 | * 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
22 | * 02110-1301, USA. | ||
23 | * | ||
24 | * | ||
25 | * This implements an ethernet device for the i2400m. | ||
26 | * | ||
27 | * We fake being an ethernet device to simplify the support from user | ||
28 | * space and from the other side. The world is (sadly) configured to | ||
29 | * take in only Ethernet devices... | ||
30 | * | ||
31 | * Because of this, currently there is an copy-each-rxed-packet | ||
32 | * overhead on the RX path. Each IP packet has to be reallocated to | ||
33 | * add an ethernet header (as there is no space in what we get from | ||
34 | * the device). This is a known drawback and coming versions of the | ||
35 | * device's firmware are being changed to add header space that can be | ||
36 | * used to insert the ethernet header without having to reallocate and | ||
37 | * copy. | ||
38 | * | ||
39 | * TX error handling is tricky; because we have to FIFO/queue the | ||
40 | * buffers for transmission (as the hardware likes it aggregated), we | ||
41 | * just give the skb to the TX subsystem and by the time it is | ||
42 | * transmitted, we have long forgotten about it. So we just don't care | ||
43 | * too much about it. | ||
44 | * | ||
45 | * Note that when the device is in idle mode with the basestation, we | ||
46 | * need to negotiate coming back up online. That involves negotiation | ||
47 | * and possible user space interaction. Thus, we defer to a workqueue | ||
48 | * to do all that. By default, we only queue a single packet and drop | ||
49 | * the rest, as potentially the time to go back from idle to normal is | ||
50 | * long. | ||
51 | * | ||
52 | * ROADMAP | ||
53 | * | ||
54 | * i2400m_open Called on ifconfig up | ||
55 | * i2400m_stop Called on ifconfig down | ||
56 | * | ||
57 | * i2400m_hard_start_xmit Called by the network stack to send a packet | ||
58 | * i2400m_net_wake_tx Wake up device from basestation-IDLE & TX | ||
59 | * i2400m_wake_tx_work | ||
60 | * i2400m_cmd_exit_idle | ||
61 | * i2400m_tx | ||
62 | * i2400m_net_tx TX a data frame | ||
63 | * i2400m_tx | ||
64 | * | ||
65 | * i2400m_change_mtu Called on ifconfig mtu XXX | ||
66 | * | ||
67 | * i2400m_tx_timeout Called when the device times out | ||
68 | * | ||
69 | * i2400m_net_rx Called by the RX code when a data frame is | ||
70 | * available. | ||
71 | * i2400m_netdev_setup Called to setup all the netdev stuff from | ||
72 | * alloc_netdev. | ||
73 | */ | ||
74 | #include <linux/if_arp.h> | ||
75 | #include <linux/netdevice.h> | ||
76 | #include "i2400m.h" | ||
77 | |||
78 | |||
79 | #define D_SUBMODULE netdev | ||
80 | #include "debug-levels.h" | ||
81 | |||
82 | enum { | ||
83 | /* netdev interface */ | ||
84 | /* | ||
85 | * Out of NWG spec (R1_v1.2.2), 3.3.3 ASN Bearer Plane MTU Size | ||
86 | * | ||
87 | * The MTU is 1400 or less | ||
88 | */ | ||
89 | I2400M_MAX_MTU = 1400, | ||
90 | I2400M_TX_TIMEOUT = HZ, | ||
91 | I2400M_TX_QLEN = 5, | ||
92 | }; | ||
93 | |||
94 | |||
95 | static | ||
96 | int i2400m_open(struct net_device *net_dev) | ||
97 | { | ||
98 | int result; | ||
99 | struct i2400m *i2400m = net_dev_to_i2400m(net_dev); | ||
100 | struct device *dev = i2400m_dev(i2400m); | ||
101 | |||
102 | d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m); | ||
103 | if (i2400m->ready == 0) { | ||
104 | dev_err(dev, "Device is still initializing\n"); | ||
105 | result = -EBUSY; | ||
106 | } else | ||
107 | result = 0; | ||
108 | d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", | ||
109 | net_dev, i2400m, result); | ||
110 | return result; | ||
111 | } | ||
112 | |||
113 | |||
114 | /* | ||
115 | * | ||
116 | * On kernel versions where cancel_work_sync() didn't return anything, | ||
117 | * we rely on wake_tx_skb() being non-NULL. | ||
118 | */ | ||
119 | static | ||
120 | int i2400m_stop(struct net_device *net_dev) | ||
121 | { | ||
122 | struct i2400m *i2400m = net_dev_to_i2400m(net_dev); | ||
123 | struct device *dev = i2400m_dev(i2400m); | ||
124 | |||
125 | d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m); | ||
126 | /* See i2400m_hard_start_xmit(), references are taken there | ||
127 | * and here we release them if the work was still | ||
128 | * pending. Note we can't differentiate work not pending vs | ||
129 | * never scheduled, so the NULL check does that. */ | ||
130 | if (cancel_work_sync(&i2400m->wake_tx_ws) == 0 | ||
131 | && i2400m->wake_tx_skb != NULL) { | ||
132 | unsigned long flags; | ||
133 | struct sk_buff *wake_tx_skb; | ||
134 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
135 | wake_tx_skb = i2400m->wake_tx_skb; /* compat help */ | ||
136 | i2400m->wake_tx_skb = NULL; /* compat help */ | ||
137 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
138 | i2400m_put(i2400m); | ||
139 | kfree_skb(wake_tx_skb); | ||
140 | } | ||
141 | d_fnend(3, dev, "(net_dev %p [i2400m %p]) = 0\n", net_dev, i2400m); | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | |||
146 | /* | ||
147 | * Wake up the device and transmit a held SKB, then restart the net queue | ||
148 | * | ||
149 | * When the device goes into basestation-idle mode, we need to tell it | ||
150 | * to exit that mode; it will negotiate with the base station, user | ||
151 | * space may have to intervene to rehandshake crypto and then tell us | ||
152 | * when it is ready to transmit the packet we have "queued". Still we | ||
153 | * need to give it sometime after it reports being ok. | ||
154 | * | ||
155 | * On error, there is not much we can do. If the error was on TX, we | ||
156 | * still wake the queue up to see if the next packet will be luckier. | ||
157 | * | ||
158 | * If _cmd_exit_idle() fails...well, it could be many things; most | ||
159 | * commonly it is that something else took the device out of IDLE mode | ||
160 | * (for example, the base station). In that case we get an -EILSEQ and | ||
161 | * we are just going to ignore that one. If the device is back to | ||
162 | * connected, then fine -- if it is someother state, the packet will | ||
163 | * be dropped anyway. | ||
164 | */ | ||
165 | void i2400m_wake_tx_work(struct work_struct *ws) | ||
166 | { | ||
167 | int result; | ||
168 | struct i2400m *i2400m = container_of(ws, struct i2400m, wake_tx_ws); | ||
169 | struct device *dev = i2400m_dev(i2400m); | ||
170 | struct sk_buff *skb = i2400m->wake_tx_skb; | ||
171 | unsigned long flags; | ||
172 | |||
173 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
174 | skb = i2400m->wake_tx_skb; | ||
175 | i2400m->wake_tx_skb = NULL; | ||
176 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
177 | |||
178 | d_fnstart(3, dev, "(ws %p i2400m %p skb %p)\n", ws, i2400m, skb); | ||
179 | result = -EINVAL; | ||
180 | if (skb == NULL) { | ||
181 | dev_err(dev, "WAKE&TX: skb dissapeared!\n"); | ||
182 | goto out_put; | ||
183 | } | ||
184 | result = i2400m_cmd_exit_idle(i2400m); | ||
185 | if (result == -EILSEQ) | ||
186 | result = 0; | ||
187 | if (result < 0) { | ||
188 | dev_err(dev, "WAKE&TX: device didn't get out of idle: " | ||
189 | "%d\n", result); | ||
190 | goto error; | ||
191 | } | ||
192 | result = wait_event_timeout(i2400m->state_wq, | ||
193 | i2400m->state != I2400M_SS_IDLE, 5 * HZ); | ||
194 | if (result == 0) | ||
195 | result = -ETIMEDOUT; | ||
196 | if (result < 0) { | ||
197 | dev_err(dev, "WAKE&TX: error waiting for device to exit IDLE: " | ||
198 | "%d\n", result); | ||
199 | goto error; | ||
200 | } | ||
201 | msleep(20); /* device still needs some time or it drops it */ | ||
202 | result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA); | ||
203 | netif_wake_queue(i2400m->wimax_dev.net_dev); | ||
204 | error: | ||
205 | kfree_skb(skb); /* refcount transferred by _hard_start_xmit() */ | ||
206 | out_put: | ||
207 | i2400m_put(i2400m); | ||
208 | d_fnend(3, dev, "(ws %p i2400m %p skb %p) = void [%d]\n", | ||
209 | ws, i2400m, skb, result); | ||
210 | } | ||
211 | |||
212 | |||
213 | /* | ||
214 | * Prepare the data payload TX header | ||
215 | * | ||
216 | * The i2400m expects a 4 byte header in front of a data packet. | ||
217 | * | ||
218 | * Because we pretend to be an ethernet device, this packet comes with | ||
219 | * an ethernet header. Pull it and push our header. | ||
220 | */ | ||
221 | static | ||
222 | void i2400m_tx_prep_header(struct sk_buff *skb) | ||
223 | { | ||
224 | struct i2400m_pl_data_hdr *pl_hdr; | ||
225 | skb_pull(skb, ETH_HLEN); | ||
226 | pl_hdr = (struct i2400m_pl_data_hdr *) skb_push(skb, sizeof(*pl_hdr)); | ||
227 | pl_hdr->reserved = 0; | ||
228 | } | ||
229 | |||
230 | |||
231 | /* | ||
232 | * TX an skb to an idle device | ||
233 | * | ||
234 | * When the device is in basestation-idle mode, we need to wake it up | ||
235 | * and then TX. So we queue a work_struct for doing so. | ||
236 | * | ||
237 | * We need to get an extra ref for the skb (so it is not dropped), as | ||
238 | * well as be careful not to queue more than one request (won't help | ||
239 | * at all). If more than one request comes or there are errors, we | ||
240 | * just drop the packets (see i2400m_hard_start_xmit()). | ||
241 | */ | ||
242 | static | ||
243 | int i2400m_net_wake_tx(struct i2400m *i2400m, struct net_device *net_dev, | ||
244 | struct sk_buff *skb) | ||
245 | { | ||
246 | int result; | ||
247 | struct device *dev = i2400m_dev(i2400m); | ||
248 | unsigned long flags; | ||
249 | |||
250 | d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev); | ||
251 | if (net_ratelimit()) { | ||
252 | d_printf(3, dev, "WAKE&NETTX: " | ||
253 | "skb %p sending %d bytes to radio\n", | ||
254 | skb, skb->len); | ||
255 | d_dump(4, dev, skb->data, skb->len); | ||
256 | } | ||
257 | /* We hold a ref count for i2400m and skb, so when | ||
258 | * stopping() the device, we need to cancel that work | ||
259 | * and if pending, release those resources. */ | ||
260 | result = 0; | ||
261 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
262 | if (!work_pending(&i2400m->wake_tx_ws)) { | ||
263 | netif_stop_queue(net_dev); | ||
264 | i2400m_get(i2400m); | ||
265 | i2400m->wake_tx_skb = skb_get(skb); /* transfer ref count */ | ||
266 | i2400m_tx_prep_header(skb); | ||
267 | result = schedule_work(&i2400m->wake_tx_ws); | ||
268 | WARN_ON(result == 0); | ||
269 | } | ||
270 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
271 | if (result == 0) { | ||
272 | /* Yes, this happens even if we stopped the | ||
273 | * queue -- blame the queue disciplines that | ||
274 | * queue without looking -- I guess there is a reason | ||
275 | * for that. */ | ||
276 | if (net_ratelimit()) | ||
277 | d_printf(1, dev, "NETTX: device exiting idle, " | ||
278 | "dropping skb %p, queue running %d\n", | ||
279 | skb, netif_queue_stopped(net_dev)); | ||
280 | result = -EBUSY; | ||
281 | } | ||
282 | d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result); | ||
283 | return result; | ||
284 | } | ||
285 | |||
286 | |||
287 | /* | ||
288 | * Transmit a packet to the base station on behalf of the network stack. | ||
289 | * | ||
290 | * Returns: 0 if ok, < 0 errno code on error. | ||
291 | * | ||
292 | * We need to pull the ethernet header and add the hardware header, | ||
293 | * which is currently set to all zeroes and reserved. | ||
294 | */ | ||
295 | static | ||
296 | int i2400m_net_tx(struct i2400m *i2400m, struct net_device *net_dev, | ||
297 | struct sk_buff *skb) | ||
298 | { | ||
299 | int result; | ||
300 | struct device *dev = i2400m_dev(i2400m); | ||
301 | |||
302 | d_fnstart(3, dev, "(i2400m %p net_dev %p skb %p)\n", | ||
303 | i2400m, net_dev, skb); | ||
304 | /* FIXME: check eth hdr, only IPv4 is routed by the device as of now */ | ||
305 | net_dev->trans_start = jiffies; | ||
306 | i2400m_tx_prep_header(skb); | ||
307 | d_printf(3, dev, "NETTX: skb %p sending %d bytes to radio\n", | ||
308 | skb, skb->len); | ||
309 | d_dump(4, dev, skb->data, skb->len); | ||
310 | result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA); | ||
311 | d_fnend(3, dev, "(i2400m %p net_dev %p skb %p) = %d\n", | ||
312 | i2400m, net_dev, skb, result); | ||
313 | return result; | ||
314 | } | ||
315 | |||
316 | |||
317 | /* | ||
318 | * Transmit a packet to the base station on behalf of the network stack | ||
319 | * | ||
320 | * | ||
321 | * Returns: NETDEV_TX_OK (always, even in case of error) | ||
322 | * | ||
323 | * In case of error, we just drop it. Reasons: | ||
324 | * | ||
325 | * - we add a hw header to each skb, and if the network stack | ||
326 | * retries, we have no way to know if that skb has it or not. | ||
327 | * | ||
328 | * - network protocols have their own drop-recovery mechanisms | ||
329 | * | ||
330 | * - there is not much else we can do | ||
331 | * | ||
332 | * If the device is idle, we need to wake it up; that is an operation | ||
333 | * that will sleep. See i2400m_net_wake_tx() for details. | ||
334 | */ | ||
335 | static | ||
336 | int i2400m_hard_start_xmit(struct sk_buff *skb, | ||
337 | struct net_device *net_dev) | ||
338 | { | ||
339 | int result; | ||
340 | struct i2400m *i2400m = net_dev_to_i2400m(net_dev); | ||
341 | struct device *dev = i2400m_dev(i2400m); | ||
342 | |||
343 | d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev); | ||
344 | if (i2400m->state == I2400M_SS_IDLE) | ||
345 | result = i2400m_net_wake_tx(i2400m, net_dev, skb); | ||
346 | else | ||
347 | result = i2400m_net_tx(i2400m, net_dev, skb); | ||
348 | if (result < 0) | ||
349 | net_dev->stats.tx_dropped++; | ||
350 | else { | ||
351 | net_dev->stats.tx_packets++; | ||
352 | net_dev->stats.tx_bytes += skb->len; | ||
353 | } | ||
354 | kfree_skb(skb); | ||
355 | result = NETDEV_TX_OK; | ||
356 | d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result); | ||
357 | return result; | ||
358 | } | ||
359 | |||
360 | |||
361 | static | ||
362 | int i2400m_change_mtu(struct net_device *net_dev, int new_mtu) | ||
363 | { | ||
364 | int result; | ||
365 | struct i2400m *i2400m = net_dev_to_i2400m(net_dev); | ||
366 | struct device *dev = i2400m_dev(i2400m); | ||
367 | |||
368 | if (new_mtu >= I2400M_MAX_MTU) { | ||
369 | dev_err(dev, "Cannot change MTU to %d (max is %d)\n", | ||
370 | new_mtu, I2400M_MAX_MTU); | ||
371 | result = -EINVAL; | ||
372 | } else { | ||
373 | net_dev->mtu = new_mtu; | ||
374 | result = 0; | ||
375 | } | ||
376 | return result; | ||
377 | } | ||
378 | |||
379 | |||
380 | static | ||
381 | void i2400m_tx_timeout(struct net_device *net_dev) | ||
382 | { | ||
383 | /* | ||
384 | * We might want to kick the device | ||
385 | * | ||
386 | * There is not much we can do though, as the device requires | ||
387 | * that we send the data aggregated. By the time we receive | ||
388 | * this, there might be data pending to be sent or not... | ||
389 | */ | ||
390 | net_dev->stats.tx_errors++; | ||
391 | return; | ||
392 | } | ||
393 | |||
394 | |||
395 | /* | ||
396 | * Create a fake ethernet header | ||
397 | * | ||
398 | * For emulating an ethernet device, every received IP header has to | ||
399 | * be prefixed with an ethernet header. | ||
400 | * | ||
401 | * What we receive has (potentially) many IP packets concatenated with | ||
402 | * no ETH_HLEN bytes prefixed. Thus there is no space for an eth | ||
403 | * header. | ||
404 | * | ||
405 | * We would have to reallocate or do ugly fragment tricks in order to | ||
406 | * add it. | ||
407 | * | ||
408 | * But what we do is use the header space of the RX transaction | ||
409 | * (*msg_hdr) as we don't need it anymore; then we'll point all the | ||
410 | * data skbs there, as they share the same backing store. | ||
411 | * | ||
412 | * We only support IPv4 for v3 firmware. | ||
413 | */ | ||
414 | static | ||
415 | void i2400m_rx_fake_eth_header(struct net_device *net_dev, | ||
416 | void *_eth_hdr) | ||
417 | { | ||
418 | struct ethhdr *eth_hdr = _eth_hdr; | ||
419 | |||
420 | memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest)); | ||
421 | memset(eth_hdr->h_source, 0, sizeof(eth_hdr->h_dest)); | ||
422 | eth_hdr->h_proto = __constant_cpu_to_be16(ETH_P_IP); | ||
423 | } | ||
424 | |||
425 | |||
426 | /* | ||
427 | * i2400m_net_rx - pass a network packet to the stack | ||
428 | * | ||
429 | * @i2400m: device instance | ||
430 | * @skb_rx: the skb where the buffer pointed to by @buf is | ||
431 | * @i: 1 if payload is the only one | ||
432 | * @buf: pointer to the buffer containing the data | ||
433 | * @len: buffer's length | ||
434 | * | ||
435 | * We just clone the skb and set it up so that it's skb->data pointer | ||
436 | * points to "buf" and it's length. | ||
437 | * | ||
438 | * Note that if the payload is the last (or the only one) in a | ||
439 | * multi-payload message, we don't clone the SKB but just reuse it. | ||
440 | * | ||
441 | * This function is normally run from a thread context. However, we | ||
442 | * still use netif_rx() instead of netif_receive_skb() as was | ||
443 | * recommended in the mailing list. Reason is in some stress tests | ||
444 | * when sending/receiving a lot of data we seem to hit a softlock in | ||
445 | * the kernel's TCP implementation [aroudn tcp_delay_timer()]. Using | ||
446 | * netif_rx() took care of the issue. | ||
447 | * | ||
448 | * This is, of course, still open to do more research on why running | ||
449 | * with netif_receive_skb() hits this softlock. FIXME. | ||
450 | * | ||
451 | * FIXME: currently we don't do any efforts at distinguishing if what | ||
452 | * we got was an IPv4 or IPv6 header, to setup the protocol field | ||
453 | * correctly. | ||
454 | */ | ||
455 | void i2400m_net_rx(struct i2400m *i2400m, struct sk_buff *skb_rx, | ||
456 | unsigned i, const void *buf, int buf_len) | ||
457 | { | ||
458 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
459 | struct device *dev = i2400m_dev(i2400m); | ||
460 | struct sk_buff *skb; | ||
461 | |||
462 | d_fnstart(2, dev, "(i2400m %p buf %p buf_len %d)\n", | ||
463 | i2400m, buf, buf_len); | ||
464 | if (i) { | ||
465 | skb = skb_get(skb_rx); | ||
466 | d_printf(2, dev, "RX: reusing first payload skb %p\n", skb); | ||
467 | skb_pull(skb, buf - (void *) skb->data); | ||
468 | skb_trim(skb, (void *) skb_end_pointer(skb) - buf); | ||
469 | } else { | ||
470 | /* Yes, this is bad -- a lot of overhead -- see | ||
471 | * comments at the top of the file */ | ||
472 | skb = __netdev_alloc_skb(net_dev, buf_len, GFP_KERNEL); | ||
473 | if (skb == NULL) { | ||
474 | dev_err(dev, "NETRX: no memory to realloc skb\n"); | ||
475 | net_dev->stats.rx_dropped++; | ||
476 | goto error_skb_realloc; | ||
477 | } | ||
478 | memcpy(skb_put(skb, buf_len), buf, buf_len); | ||
479 | } | ||
480 | i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev, | ||
481 | skb->data - ETH_HLEN); | ||
482 | skb_set_mac_header(skb, -ETH_HLEN); | ||
483 | skb->dev = i2400m->wimax_dev.net_dev; | ||
484 | skb->protocol = htons(ETH_P_IP); | ||
485 | net_dev->stats.rx_packets++; | ||
486 | net_dev->stats.rx_bytes += buf_len; | ||
487 | d_printf(3, dev, "NETRX: receiving %d bytes to network stack\n", | ||
488 | buf_len); | ||
489 | d_dump(4, dev, buf, buf_len); | ||
490 | netif_rx_ni(skb); /* see notes in function header */ | ||
491 | error_skb_realloc: | ||
492 | d_fnend(2, dev, "(i2400m %p buf %p buf_len %d) = void\n", | ||
493 | i2400m, buf, buf_len); | ||
494 | } | ||
495 | |||
496 | |||
497 | /** | ||
498 | * i2400m_netdev_setup - Setup setup @net_dev's i2400m private data | ||
499 | * | ||
500 | * Called by alloc_netdev() | ||
501 | */ | ||
502 | void i2400m_netdev_setup(struct net_device *net_dev) | ||
503 | { | ||
504 | d_fnstart(3, NULL, "(net_dev %p)\n", net_dev); | ||
505 | ether_setup(net_dev); | ||
506 | net_dev->mtu = I2400M_MAX_MTU; | ||
507 | net_dev->tx_queue_len = I2400M_TX_QLEN; | ||
508 | net_dev->features = | ||
509 | NETIF_F_VLAN_CHALLENGED | ||
510 | | NETIF_F_HIGHDMA; | ||
511 | net_dev->flags = | ||
512 | IFF_NOARP /* i2400m is apure IP device */ | ||
513 | & (~IFF_BROADCAST /* i2400m is P2P */ | ||
514 | & ~IFF_MULTICAST); | ||
515 | net_dev->watchdog_timeo = I2400M_TX_TIMEOUT; | ||
516 | net_dev->open = i2400m_open; | ||
517 | net_dev->stop = i2400m_stop; | ||
518 | net_dev->hard_start_xmit = i2400m_hard_start_xmit; | ||
519 | net_dev->change_mtu = i2400m_change_mtu; | ||
520 | net_dev->tx_timeout = i2400m_tx_timeout; | ||
521 | d_fnend(3, NULL, "(net_dev %p) = void\n", net_dev); | ||
522 | } | ||
523 | EXPORT_SYMBOL_GPL(i2400m_netdev_setup); | ||
524 | |||
diff --git a/drivers/net/wimax/i2400m/op-rfkill.c b/drivers/net/wimax/i2400m/op-rfkill.c new file mode 100644 index 000000000000..487ec58cea46 --- /dev/null +++ b/drivers/net/wimax/i2400m/op-rfkill.c | |||
@@ -0,0 +1,207 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Implement backend for the WiMAX stack rfkill support | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | * | ||
23 | * | ||
24 | * The WiMAX kernel stack integrates into RF-Kill and keeps the | ||
25 | * switches's status. We just need to: | ||
26 | * | ||
27 | * - report changes in the HW RF Kill switch [with | ||
28 | * wimax_rfkill_{sw,hw}_report(), which happens when we detect those | ||
29 | * indications coming through hardware reports]. We also do it on | ||
30 | * initialization to let the stack know the intial HW state. | ||
31 | * | ||
32 | * - implement indications from the stack to change the SW RF Kill | ||
33 | * switch (coming from sysfs, the wimax stack or user space). | ||
34 | */ | ||
35 | #include "i2400m.h" | ||
36 | #include <linux/wimax/i2400m.h> | ||
37 | |||
38 | |||
39 | |||
40 | #define D_SUBMODULE rfkill | ||
41 | #include "debug-levels.h" | ||
42 | |||
43 | /* | ||
44 | * Return true if the i2400m radio is in the requested wimax_rf_state state | ||
45 | * | ||
46 | */ | ||
47 | static | ||
48 | int i2400m_radio_is(struct i2400m *i2400m, enum wimax_rf_state state) | ||
49 | { | ||
50 | if (state == WIMAX_RF_OFF) | ||
51 | return i2400m->state == I2400M_SS_RF_OFF | ||
52 | || i2400m->state == I2400M_SS_RF_SHUTDOWN; | ||
53 | else if (state == WIMAX_RF_ON) | ||
54 | /* state == WIMAX_RF_ON */ | ||
55 | return i2400m->state != I2400M_SS_RF_OFF | ||
56 | && i2400m->state != I2400M_SS_RF_SHUTDOWN; | ||
57 | else | ||
58 | BUG(); | ||
59 | } | ||
60 | |||
61 | |||
62 | /* | ||
63 | * WiMAX stack operation: implement SW RFKill toggling | ||
64 | * | ||
65 | * @wimax_dev: device descriptor | ||
66 | * @skb: skb where the message has been received; skb->data is | ||
67 | * expected to point to the message payload. | ||
68 | * @genl_info: passed by the generic netlink layer | ||
69 | * | ||
70 | * Generic Netlink will call this function when a message is sent from | ||
71 | * userspace to change the software RF-Kill switch status. | ||
72 | * | ||
73 | * This function will set the device's sofware RF-Kill switch state to | ||
74 | * match what is requested. | ||
75 | * | ||
76 | * NOTE: the i2400m has a strict state machine; we can only set the | ||
77 | * RF-Kill switch when it is on, the HW RF-Kill is on and the | ||
78 | * device is initialized. So we ignore errors steaming from not | ||
79 | * being in the right state (-EILSEQ). | ||
80 | */ | ||
81 | int i2400m_op_rfkill_sw_toggle(struct wimax_dev *wimax_dev, | ||
82 | enum wimax_rf_state state) | ||
83 | { | ||
84 | int result; | ||
85 | struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); | ||
86 | struct device *dev = i2400m_dev(i2400m); | ||
87 | struct sk_buff *ack_skb; | ||
88 | struct { | ||
89 | struct i2400m_l3l4_hdr hdr; | ||
90 | struct i2400m_tlv_rf_operation sw_rf; | ||
91 | } __attribute__((packed)) *cmd; | ||
92 | char strerr[32]; | ||
93 | |||
94 | d_fnstart(4, dev, "(wimax_dev %p state %d)\n", wimax_dev, state); | ||
95 | |||
96 | result = -ENOMEM; | ||
97 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
98 | if (cmd == NULL) | ||
99 | goto error_alloc; | ||
100 | cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_RF_CONTROL); | ||
101 | cmd->hdr.length = sizeof(cmd->sw_rf); | ||
102 | cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION); | ||
103 | cmd->sw_rf.hdr.type = cpu_to_le16(I2400M_TLV_RF_OPERATION); | ||
104 | cmd->sw_rf.hdr.length = cpu_to_le16(sizeof(cmd->sw_rf.status)); | ||
105 | switch (state) { | ||
106 | case WIMAX_RF_OFF: /* RFKILL ON, radio OFF */ | ||
107 | cmd->sw_rf.status = cpu_to_le32(2); | ||
108 | break; | ||
109 | case WIMAX_RF_ON: /* RFKILL OFF, radio ON */ | ||
110 | cmd->sw_rf.status = cpu_to_le32(1); | ||
111 | break; | ||
112 | default: | ||
113 | BUG(); | ||
114 | } | ||
115 | |||
116 | ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); | ||
117 | result = PTR_ERR(ack_skb); | ||
118 | if (IS_ERR(ack_skb)) { | ||
119 | dev_err(dev, "Failed to issue 'RF Control' command: %d\n", | ||
120 | result); | ||
121 | goto error_msg_to_dev; | ||
122 | } | ||
123 | result = i2400m_msg_check_status(wimax_msg_data(ack_skb), | ||
124 | strerr, sizeof(strerr)); | ||
125 | if (result < 0) { | ||
126 | dev_err(dev, "'RF Control' (0x%04x) command failed: %d - %s\n", | ||
127 | I2400M_MT_CMD_RF_CONTROL, result, strerr); | ||
128 | goto error_cmd; | ||
129 | } | ||
130 | |||
131 | /* Now we wait for the state to change to RADIO_OFF or RADIO_ON */ | ||
132 | result = wait_event_timeout( | ||
133 | i2400m->state_wq, i2400m_radio_is(i2400m, state), | ||
134 | 5 * HZ); | ||
135 | if (result == 0) | ||
136 | result = -ETIMEDOUT; | ||
137 | if (result < 0) | ||
138 | dev_err(dev, "Error waiting for device to toggle RF state: " | ||
139 | "%d\n", result); | ||
140 | result = 0; | ||
141 | error_cmd: | ||
142 | kfree_skb(ack_skb); | ||
143 | error_msg_to_dev: | ||
144 | error_alloc: | ||
145 | d_fnend(4, dev, "(wimax_dev %p state %d) = %d\n", | ||
146 | wimax_dev, state, result); | ||
147 | return result; | ||
148 | } | ||
149 | |||
150 | |||
151 | /* | ||
152 | * Inform the WiMAX stack of changes in the RF Kill switches reported | ||
153 | * by the device | ||
154 | * | ||
155 | * @i2400m: device descriptor | ||
156 | * @rfss: TLV for RF Switches status; already validated | ||
157 | * | ||
158 | * NOTE: the reports on RF switch status cannot be trusted | ||
159 | * or used until the device is in a state of RADIO_OFF | ||
160 | * or greater. | ||
161 | */ | ||
162 | void i2400m_report_tlv_rf_switches_status( | ||
163 | struct i2400m *i2400m, | ||
164 | const struct i2400m_tlv_rf_switches_status *rfss) | ||
165 | { | ||
166 | struct device *dev = i2400m_dev(i2400m); | ||
167 | enum i2400m_rf_switch_status hw, sw; | ||
168 | enum wimax_st wimax_state; | ||
169 | |||
170 | sw = le32_to_cpu(rfss->sw_rf_switch); | ||
171 | hw = le32_to_cpu(rfss->hw_rf_switch); | ||
172 | |||
173 | d_fnstart(3, dev, "(i2400m %p rfss %p [hw %u sw %u])\n", | ||
174 | i2400m, rfss, hw, sw); | ||
175 | /* We only process rw switch evens when the device has been | ||
176 | * fully initialized */ | ||
177 | wimax_state = wimax_state_get(&i2400m->wimax_dev); | ||
178 | if (wimax_state < WIMAX_ST_RADIO_OFF) { | ||
179 | d_printf(3, dev, "ignoring RF switches report, state %u\n", | ||
180 | wimax_state); | ||
181 | goto out; | ||
182 | } | ||
183 | switch (sw) { | ||
184 | case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */ | ||
185 | wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_ON); | ||
186 | break; | ||
187 | case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */ | ||
188 | wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_OFF); | ||
189 | break; | ||
190 | default: | ||
191 | dev_err(dev, "HW BUG? Unknown RF SW state 0x%x\n", sw); | ||
192 | } | ||
193 | |||
194 | switch (hw) { | ||
195 | case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */ | ||
196 | wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_ON); | ||
197 | break; | ||
198 | case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */ | ||
199 | wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_OFF); | ||
200 | break; | ||
201 | default: | ||
202 | dev_err(dev, "HW BUG? Unknown RF HW state 0x%x\n", hw); | ||
203 | } | ||
204 | out: | ||
205 | d_fnend(3, dev, "(i2400m %p rfss %p [hw %u sw %u]) = void\n", | ||
206 | i2400m, rfss, hw, sw); | ||
207 | } | ||
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c new file mode 100644 index 000000000000..6922022710ac --- /dev/null +++ b/drivers/net/wimax/i2400m/rx.c | |||
@@ -0,0 +1,534 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Handle incoming traffic and deliver it to the control or data planes | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * - Initial implementation | ||
38 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
39 | * - Use skb_clone(), break up processing in chunks | ||
40 | * - Split transport/device specific | ||
41 | * - Make buffer size dynamic to exert less memory pressure | ||
42 | * | ||
43 | * | ||
44 | * This handles the RX path. | ||
45 | * | ||
46 | * We receive an RX message from the bus-specific driver, which | ||
47 | * contains one or more payloads that have potentially different | ||
48 | * destinataries (data or control paths). | ||
49 | * | ||
50 | * So we just take that payload from the transport specific code in | ||
51 | * the form of an skb, break it up in chunks (a cloned skb each in the | ||
52 | * case of network packets) and pass it to netdev or to the | ||
53 | * command/ack handler (and from there to the WiMAX stack). | ||
54 | * | ||
55 | * PROTOCOL FORMAT | ||
56 | * | ||
57 | * The format of the buffer is: | ||
58 | * | ||
59 | * HEADER (struct i2400m_msg_hdr) | ||
60 | * PAYLOAD DESCRIPTOR 0 (struct i2400m_pld) | ||
61 | * PAYLOAD DESCRIPTOR 1 | ||
62 | * ... | ||
63 | * PAYLOAD DESCRIPTOR N | ||
64 | * PAYLOAD 0 (raw bytes) | ||
65 | * PAYLOAD 1 | ||
66 | * ... | ||
67 | * PAYLOAD N | ||
68 | * | ||
69 | * See tx.c for a deeper description on alignment requirements and | ||
70 | * other fun facts of it. | ||
71 | * | ||
72 | * ROADMAP | ||
73 | * | ||
74 | * i2400m_rx | ||
75 | * i2400m_rx_msg_hdr_check | ||
76 | * i2400m_rx_pl_descr_check | ||
77 | * i2400m_rx_payload | ||
78 | * i2400m_net_rx | ||
79 | * i2400m_rx_ctl | ||
80 | * i2400m_msg_size_check | ||
81 | * i2400m_report_hook_work [in a workqueue] | ||
82 | * i2400m_report_hook | ||
83 | * wimax_msg_to_user | ||
84 | * i2400m_rx_ctl_ack | ||
85 | * wimax_msg_to_user_alloc | ||
86 | * i2400m_rx_trace | ||
87 | * i2400m_msg_size_check | ||
88 | * wimax_msg | ||
89 | */ | ||
90 | #include <linux/kernel.h> | ||
91 | #include <linux/if_arp.h> | ||
92 | #include <linux/netdevice.h> | ||
93 | #include <linux/workqueue.h> | ||
94 | #include "i2400m.h" | ||
95 | |||
96 | |||
97 | #define D_SUBMODULE rx | ||
98 | #include "debug-levels.h" | ||
99 | |||
100 | struct i2400m_report_hook_args { | ||
101 | struct sk_buff *skb_rx; | ||
102 | const struct i2400m_l3l4_hdr *l3l4_hdr; | ||
103 | size_t size; | ||
104 | }; | ||
105 | |||
106 | |||
107 | /* | ||
108 | * Execute i2400m_report_hook in a workqueue | ||
109 | * | ||
110 | * Unpacks arguments from the deferred call, executes it and then | ||
111 | * drops the references. | ||
112 | * | ||
113 | * Obvious NOTE: References are needed because we are a separate | ||
114 | * thread; otherwise the buffer changes under us because it is | ||
115 | * released by the original caller. | ||
116 | */ | ||
117 | static | ||
118 | void i2400m_report_hook_work(struct work_struct *ws) | ||
119 | { | ||
120 | struct i2400m_work *iw = | ||
121 | container_of(ws, struct i2400m_work, ws); | ||
122 | struct i2400m_report_hook_args *args = (void *) iw->pl; | ||
123 | i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); | ||
124 | kfree_skb(args->skb_rx); | ||
125 | i2400m_put(iw->i2400m); | ||
126 | kfree(iw); | ||
127 | } | ||
128 | |||
129 | |||
130 | /* | ||
131 | * Process an ack to a command | ||
132 | * | ||
133 | * @i2400m: device descriptor | ||
134 | * @payload: pointer to message | ||
135 | * @size: size of the message | ||
136 | * | ||
137 | * Pass the acknodledgment (in an skb) to the thread that is waiting | ||
138 | * for it in i2400m->msg_completion. | ||
139 | * | ||
140 | * We need to coordinate properly with the thread waiting for the | ||
141 | * ack. Check if it is waiting or if it is gone. We loose the spinlock | ||
142 | * to avoid allocating on atomic contexts (yeah, could use GFP_ATOMIC, | ||
143 | * but this is not so speed critical). | ||
144 | */ | ||
145 | static | ||
146 | void i2400m_rx_ctl_ack(struct i2400m *i2400m, | ||
147 | const void *payload, size_t size) | ||
148 | { | ||
149 | struct device *dev = i2400m_dev(i2400m); | ||
150 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
151 | unsigned long flags; | ||
152 | struct sk_buff *ack_skb; | ||
153 | |||
154 | /* Anyone waiting for an answer? */ | ||
155 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
156 | if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) { | ||
157 | dev_err(dev, "Huh? reply to command with no waiters\n"); | ||
158 | goto error_no_waiter; | ||
159 | } | ||
160 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
161 | |||
162 | ack_skb = wimax_msg_alloc(wimax_dev, NULL, payload, size, GFP_KERNEL); | ||
163 | |||
164 | /* Check waiter didn't time out waiting for the answer... */ | ||
165 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
166 | if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) { | ||
167 | d_printf(1, dev, "Huh? waiter for command reply cancelled\n"); | ||
168 | goto error_waiter_cancelled; | ||
169 | } | ||
170 | if (ack_skb == NULL) { | ||
171 | dev_err(dev, "CMD/GET/SET ack: cannot allocate SKB\n"); | ||
172 | i2400m->ack_skb = ERR_PTR(-ENOMEM); | ||
173 | } else | ||
174 | i2400m->ack_skb = ack_skb; | ||
175 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
176 | complete(&i2400m->msg_completion); | ||
177 | return; | ||
178 | |||
179 | error_waiter_cancelled: | ||
180 | if (ack_skb) | ||
181 | kfree_skb(ack_skb); | ||
182 | error_no_waiter: | ||
183 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
184 | return; | ||
185 | } | ||
186 | |||
187 | |||
188 | /* | ||
189 | * Receive and process a control payload | ||
190 | * | ||
191 | * @i2400m: device descriptor | ||
192 | * @skb_rx: skb that contains the payload (for reference counting) | ||
193 | * @payload: pointer to message | ||
194 | * @size: size of the message | ||
195 | * | ||
196 | * There are two types of control RX messages: reports (asynchronous, | ||
197 | * like your every day interrupts) and 'acks' (reponses to a command, | ||
198 | * get or set request). | ||
199 | * | ||
200 | * If it is a report, we run hooks on it (to extract information for | ||
201 | * things we need to do in the driver) and then pass it over to the | ||
202 | * WiMAX stack to send it to user space. | ||
203 | * | ||
204 | * NOTE: report processing is done in a workqueue specific to the | ||
205 | * generic driver, to avoid deadlocks in the system. | ||
206 | * | ||
207 | * If it is not a report, it is an ack to a previously executed | ||
208 | * command, set or get, so wake up whoever is waiting for it from | ||
209 | * i2400m_msg_to_dev(). i2400m_rx_ctl_ack() takes care of that. | ||
210 | * | ||
211 | * Note that the sizes we pass to other functions from here are the | ||
212 | * sizes of the _l3l4_hdr + payload, not full buffer sizes, as we have | ||
213 | * verified in _msg_size_check() that they are congruent. | ||
214 | * | ||
215 | * For reports: We can't clone the original skb where the data is | ||
216 | * because we need to send this up via netlink; netlink has to add | ||
217 | * headers and we can't overwrite what's preceeding the payload...as | ||
218 | * it is another message. So we just dup them. | ||
219 | */ | ||
220 | static | ||
221 | void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, | ||
222 | const void *payload, size_t size) | ||
223 | { | ||
224 | int result; | ||
225 | struct device *dev = i2400m_dev(i2400m); | ||
226 | const struct i2400m_l3l4_hdr *l3l4_hdr = payload; | ||
227 | unsigned msg_type; | ||
228 | |||
229 | result = i2400m_msg_size_check(i2400m, l3l4_hdr, size); | ||
230 | if (result < 0) { | ||
231 | dev_err(dev, "HW BUG? device sent a bad message: %d\n", | ||
232 | result); | ||
233 | goto error_check; | ||
234 | } | ||
235 | msg_type = le16_to_cpu(l3l4_hdr->type); | ||
236 | d_printf(1, dev, "%s 0x%04x: %zu bytes\n", | ||
237 | msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", | ||
238 | msg_type, size); | ||
239 | d_dump(2, dev, l3l4_hdr, size); | ||
240 | if (msg_type & I2400M_MT_REPORT_MASK) { | ||
241 | /* These hooks have to be ran serialized; as well, the | ||
242 | * handling might force the execution of commands, and | ||
243 | * that might cause reentrancy issues with | ||
244 | * bus-specific subdrivers and workqueues. So we run | ||
245 | * it in a separate workqueue. */ | ||
246 | struct i2400m_report_hook_args args = { | ||
247 | .skb_rx = skb_rx, | ||
248 | .l3l4_hdr = l3l4_hdr, | ||
249 | .size = size | ||
250 | }; | ||
251 | if (unlikely(i2400m->ready == 0)) /* only send if up */ | ||
252 | return; | ||
253 | skb_get(skb_rx); | ||
254 | i2400m_queue_work(i2400m, i2400m_report_hook_work, | ||
255 | GFP_KERNEL, &args, sizeof(args)); | ||
256 | result = wimax_msg(&i2400m->wimax_dev, NULL, l3l4_hdr, size, | ||
257 | GFP_KERNEL); | ||
258 | if (result < 0) | ||
259 | dev_err(dev, "error sending report to userspace: %d\n", | ||
260 | result); | ||
261 | } else /* an ack to a CMD, GET or SET */ | ||
262 | i2400m_rx_ctl_ack(i2400m, payload, size); | ||
263 | error_check: | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | |||
268 | |||
269 | |||
270 | /* | ||
271 | * Receive and send up a trace | ||
272 | * | ||
273 | * @i2400m: device descriptor | ||
274 | * @skb_rx: skb that contains the trace (for reference counting) | ||
275 | * @payload: pointer to trace message inside the skb | ||
276 | * @size: size of the message | ||
277 | * | ||
278 | * THe i2400m might produce trace information (diagnostics) and we | ||
279 | * send them through a different kernel-to-user pipe (to avoid | ||
280 | * clogging it). | ||
281 | * | ||
282 | * As in i2400m_rx_ctl(), we can't clone the original skb where the | ||
283 | * data is because we need to send this up via netlink; netlink has to | ||
284 | * add headers and we can't overwrite what's preceeding the | ||
285 | * payload...as it is another message. So we just dup them. | ||
286 | */ | ||
287 | static | ||
288 | void i2400m_rx_trace(struct i2400m *i2400m, | ||
289 | const void *payload, size_t size) | ||
290 | { | ||
291 | int result; | ||
292 | struct device *dev = i2400m_dev(i2400m); | ||
293 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
294 | const struct i2400m_l3l4_hdr *l3l4_hdr = payload; | ||
295 | unsigned msg_type; | ||
296 | |||
297 | result = i2400m_msg_size_check(i2400m, l3l4_hdr, size); | ||
298 | if (result < 0) { | ||
299 | dev_err(dev, "HW BUG? device sent a bad trace message: %d\n", | ||
300 | result); | ||
301 | goto error_check; | ||
302 | } | ||
303 | msg_type = le16_to_cpu(l3l4_hdr->type); | ||
304 | d_printf(1, dev, "Trace %s 0x%04x: %zu bytes\n", | ||
305 | msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", | ||
306 | msg_type, size); | ||
307 | d_dump(2, dev, l3l4_hdr, size); | ||
308 | if (unlikely(i2400m->ready == 0)) /* only send if up */ | ||
309 | return; | ||
310 | result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL); | ||
311 | if (result < 0) | ||
312 | dev_err(dev, "error sending trace to userspace: %d\n", | ||
313 | result); | ||
314 | error_check: | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | |||
319 | /* | ||
320 | * Act on a received payload | ||
321 | * | ||
322 | * @i2400m: device instance | ||
323 | * @skb_rx: skb where the transaction was received | ||
324 | * @single: 1 if there is only one payload, 0 otherwise | ||
325 | * @pld: payload descriptor | ||
326 | * @payload: payload data | ||
327 | * | ||
328 | * Upon reception of a payload, look at its guts in the payload | ||
329 | * descriptor and decide what to do with it. | ||
330 | */ | ||
331 | static | ||
332 | void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx, | ||
333 | unsigned single, const struct i2400m_pld *pld, | ||
334 | const void *payload) | ||
335 | { | ||
336 | struct device *dev = i2400m_dev(i2400m); | ||
337 | size_t pl_size = i2400m_pld_size(pld); | ||
338 | enum i2400m_pt pl_type = i2400m_pld_type(pld); | ||
339 | |||
340 | switch (pl_type) { | ||
341 | case I2400M_PT_DATA: | ||
342 | d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size); | ||
343 | i2400m_net_rx(i2400m, skb_rx, single, payload, pl_size); | ||
344 | break; | ||
345 | case I2400M_PT_CTRL: | ||
346 | i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size); | ||
347 | break; | ||
348 | case I2400M_PT_TRACE: | ||
349 | i2400m_rx_trace(i2400m, payload, pl_size); | ||
350 | break; | ||
351 | default: /* Anything else shouldn't come to the host */ | ||
352 | if (printk_ratelimit()) | ||
353 | dev_err(dev, "RX: HW BUG? unexpected payload type %u\n", | ||
354 | pl_type); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | |||
359 | /* | ||
360 | * Check a received transaction's message header | ||
361 | * | ||
362 | * @i2400m: device descriptor | ||
363 | * @msg_hdr: message header | ||
364 | * @buf_size: size of the received buffer | ||
365 | * | ||
366 | * Check that the declarations done by a RX buffer message header are | ||
367 | * sane and consistent with the amount of data that was received. | ||
368 | */ | ||
369 | static | ||
370 | int i2400m_rx_msg_hdr_check(struct i2400m *i2400m, | ||
371 | const struct i2400m_msg_hdr *msg_hdr, | ||
372 | size_t buf_size) | ||
373 | { | ||
374 | int result = -EIO; | ||
375 | struct device *dev = i2400m_dev(i2400m); | ||
376 | if (buf_size < sizeof(*msg_hdr)) { | ||
377 | dev_err(dev, "RX: HW BUG? message with short header (%zu " | ||
378 | "vs %zu bytes expected)\n", buf_size, sizeof(*msg_hdr)); | ||
379 | goto error; | ||
380 | } | ||
381 | if (msg_hdr->barker != cpu_to_le32(I2400M_D2H_MSG_BARKER)) { | ||
382 | dev_err(dev, "RX: HW BUG? message received with unknown " | ||
383 | "barker 0x%08x (buf_size %zu bytes)\n", | ||
384 | le32_to_cpu(msg_hdr->barker), buf_size); | ||
385 | goto error; | ||
386 | } | ||
387 | if (msg_hdr->num_pls == 0) { | ||
388 | dev_err(dev, "RX: HW BUG? zero payload packets in message\n"); | ||
389 | goto error; | ||
390 | } | ||
391 | if (le16_to_cpu(msg_hdr->num_pls) > I2400M_MAX_PLS_IN_MSG) { | ||
392 | dev_err(dev, "RX: HW BUG? message contains more payload " | ||
393 | "than maximum; ignoring.\n"); | ||
394 | goto error; | ||
395 | } | ||
396 | result = 0; | ||
397 | error: | ||
398 | return result; | ||
399 | } | ||
400 | |||
401 | |||
402 | /* | ||
403 | * Check a payload descriptor against the received data | ||
404 | * | ||
405 | * @i2400m: device descriptor | ||
406 | * @pld: payload descriptor | ||
407 | * @pl_itr: offset (in bytes) in the received buffer the payload is | ||
408 | * located | ||
409 | * @buf_size: size of the received buffer | ||
410 | * | ||
411 | * Given a payload descriptor (part of a RX buffer), check it is sane | ||
412 | * and that the data it declares fits in the buffer. | ||
413 | */ | ||
414 | static | ||
415 | int i2400m_rx_pl_descr_check(struct i2400m *i2400m, | ||
416 | const struct i2400m_pld *pld, | ||
417 | size_t pl_itr, size_t buf_size) | ||
418 | { | ||
419 | int result = -EIO; | ||
420 | struct device *dev = i2400m_dev(i2400m); | ||
421 | size_t pl_size = i2400m_pld_size(pld); | ||
422 | enum i2400m_pt pl_type = i2400m_pld_type(pld); | ||
423 | |||
424 | if (pl_size > i2400m->bus_pl_size_max) { | ||
425 | dev_err(dev, "RX: HW BUG? payload @%zu: size %zu is " | ||
426 | "bigger than maximum %zu; ignoring message\n", | ||
427 | pl_itr, pl_size, i2400m->bus_pl_size_max); | ||
428 | goto error; | ||
429 | } | ||
430 | if (pl_itr + pl_size > buf_size) { /* enough? */ | ||
431 | dev_err(dev, "RX: HW BUG? payload @%zu: size %zu " | ||
432 | "goes beyond the received buffer " | ||
433 | "size (%zu bytes); ignoring message\n", | ||
434 | pl_itr, pl_size, buf_size); | ||
435 | goto error; | ||
436 | } | ||
437 | if (pl_type >= I2400M_PT_ILLEGAL) { | ||
438 | dev_err(dev, "RX: HW BUG? illegal payload type %u; " | ||
439 | "ignoring message\n", pl_type); | ||
440 | goto error; | ||
441 | } | ||
442 | result = 0; | ||
443 | error: | ||
444 | return result; | ||
445 | } | ||
446 | |||
447 | |||
448 | /** | ||
449 | * i2400m_rx - Receive a buffer of data from the device | ||
450 | * | ||
451 | * @i2400m: device descriptor | ||
452 | * @skb: skbuff where the data has been received | ||
453 | * | ||
454 | * Parse in a buffer of data that contains an RX message sent from the | ||
455 | * device. See the file header for the format. Run all checks on the | ||
456 | * buffer header, then run over each payload's descriptors, verify | ||
457 | * their consistency and act on each payload's contents. If | ||
458 | * everything is succesful, update the device's statistics. | ||
459 | * | ||
460 | * Note: You need to set the skb to contain only the length of the | ||
461 | * received buffer; for that, use skb_trim(skb, RECEIVED_SIZE). | ||
462 | * | ||
463 | * Returns: | ||
464 | * | ||
465 | * 0 if ok, < 0 errno on error | ||
466 | * | ||
467 | * If ok, this function owns now the skb and the caller DOESN'T have | ||
468 | * to run kfree_skb() on it. However, on error, the caller still owns | ||
469 | * the skb and it is responsible for releasing it. | ||
470 | */ | ||
471 | int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb) | ||
472 | { | ||
473 | int i, result; | ||
474 | struct device *dev = i2400m_dev(i2400m); | ||
475 | const struct i2400m_msg_hdr *msg_hdr; | ||
476 | size_t pl_itr, pl_size, skb_len; | ||
477 | unsigned long flags; | ||
478 | unsigned num_pls; | ||
479 | |||
480 | skb_len = skb->len; | ||
481 | d_fnstart(4, dev, "(i2400m %p skb %p [size %zu])\n", | ||
482 | i2400m, skb, skb_len); | ||
483 | result = -EIO; | ||
484 | msg_hdr = (void *) skb->data; | ||
485 | result = i2400m_rx_msg_hdr_check(i2400m, msg_hdr, skb->len); | ||
486 | if (result < 0) | ||
487 | goto error_msg_hdr_check; | ||
488 | result = -EIO; | ||
489 | num_pls = le16_to_cpu(msg_hdr->num_pls); | ||
490 | pl_itr = sizeof(*msg_hdr) + /* Check payload descriptor(s) */ | ||
491 | num_pls * sizeof(msg_hdr->pld[0]); | ||
492 | pl_itr = ALIGN(pl_itr, I2400M_PL_PAD); | ||
493 | if (pl_itr > skb->len) { /* got all the payload descriptors? */ | ||
494 | dev_err(dev, "RX: HW BUG? message too short (%u bytes) for " | ||
495 | "%u payload descriptors (%zu each, total %zu)\n", | ||
496 | skb->len, num_pls, sizeof(msg_hdr->pld[0]), pl_itr); | ||
497 | goto error_pl_descr_short; | ||
498 | } | ||
499 | /* Walk each payload payload--check we really got it */ | ||
500 | for (i = 0; i < num_pls; i++) { | ||
501 | /* work around old gcc warnings */ | ||
502 | pl_size = i2400m_pld_size(&msg_hdr->pld[i]); | ||
503 | result = i2400m_rx_pl_descr_check(i2400m, &msg_hdr->pld[i], | ||
504 | pl_itr, skb->len); | ||
505 | if (result < 0) | ||
506 | goto error_pl_descr_check; | ||
507 | i2400m_rx_payload(i2400m, skb, num_pls == 1, &msg_hdr->pld[i], | ||
508 | skb->data + pl_itr); | ||
509 | pl_itr += ALIGN(pl_size, I2400M_PL_PAD); | ||
510 | cond_resched(); /* Don't monopolize */ | ||
511 | } | ||
512 | kfree_skb(skb); | ||
513 | /* Update device statistics */ | ||
514 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
515 | i2400m->rx_pl_num += i; | ||
516 | if (i > i2400m->rx_pl_max) | ||
517 | i2400m->rx_pl_max = i; | ||
518 | if (i < i2400m->rx_pl_min) | ||
519 | i2400m->rx_pl_min = i; | ||
520 | i2400m->rx_num++; | ||
521 | i2400m->rx_size_acc += skb->len; | ||
522 | if (skb->len < i2400m->rx_size_min) | ||
523 | i2400m->rx_size_min = skb->len; | ||
524 | if (skb->len > i2400m->rx_size_max) | ||
525 | i2400m->rx_size_max = skb->len; | ||
526 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
527 | error_pl_descr_check: | ||
528 | error_pl_descr_short: | ||
529 | error_msg_hdr_check: | ||
530 | d_fnend(4, dev, "(i2400m %p skb %p [size %zu]) = %d\n", | ||
531 | i2400m, skb, skb_len, result); | ||
532 | return result; | ||
533 | } | ||
534 | EXPORT_SYMBOL_GPL(i2400m_rx); | ||
diff --git a/drivers/net/wimax/i2400m/sdio-debug-levels.h b/drivers/net/wimax/i2400m/sdio-debug-levels.h new file mode 100644 index 000000000000..c51998741301 --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-debug-levels.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * debug levels control file for the i2400m module's | ||
3 | */ | ||
4 | #ifndef __debug_levels__h__ | ||
5 | #define __debug_levels__h__ | ||
6 | |||
7 | /* Maximum compile and run time debug level for all submodules */ | ||
8 | #define D_MODULENAME i2400m_sdio | ||
9 | #define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL | ||
10 | |||
11 | #include <linux/wimax/debug.h> | ||
12 | |||
13 | /* List of all the enabled modules */ | ||
14 | enum d_module { | ||
15 | D_SUBMODULE_DECLARE(main), | ||
16 | D_SUBMODULE_DECLARE(tx), | ||
17 | D_SUBMODULE_DECLARE(rx), | ||
18 | D_SUBMODULE_DECLARE(fw) | ||
19 | }; | ||
20 | |||
21 | |||
22 | #endif /* #ifndef __debug_levels__h__ */ | ||
diff --git a/drivers/net/wimax/i2400m/sdio-fw.c b/drivers/net/wimax/i2400m/sdio-fw.c new file mode 100644 index 000000000000..3487205d8f50 --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-fw.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Firmware uploader's SDIO specifics | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
38 | * - Initial implementation | ||
39 | * | ||
40 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
41 | * - Bus generic/specific split for USB | ||
42 | * | ||
43 | * Dirk Brandewie <dirk.j.brandewie@intel.com> | ||
44 | * - Initial implementation for SDIO | ||
45 | * | ||
46 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
47 | * - SDIO rehash for changes in the bus-driver model | ||
48 | * | ||
49 | * THE PROCEDURE | ||
50 | * | ||
51 | * See fw.c for the generic description of this procedure. | ||
52 | * | ||
53 | * This file implements only the SDIO specifics. It boils down to how | ||
54 | * to send a command and waiting for an acknowledgement from the | ||
55 | * device. We do polled reads. | ||
56 | * | ||
57 | * COMMAND EXECUTION | ||
58 | * | ||
59 | * THe generic firmware upload code will call i2400m_bus_bm_cmd_send() | ||
60 | * to send commands. | ||
61 | * | ||
62 | * The SDIO devices expects things in 256 byte blocks, so it will pad | ||
63 | * it, compute the checksum (if needed) and pass it to SDIO. | ||
64 | * | ||
65 | * ACK RECEPTION | ||
66 | * | ||
67 | * This works in polling mode -- the fw loader says when to wait for | ||
68 | * data and for that it calls i2400ms_bus_bm_wait_for_ack(). | ||
69 | * | ||
70 | * This will poll the device for data until it is received. We need to | ||
71 | * receive at least as much bytes as where asked for (although it'll | ||
72 | * always be a multiple of 256 bytes). | ||
73 | */ | ||
74 | #include <linux/mmc/sdio_func.h> | ||
75 | #include "i2400m-sdio.h" | ||
76 | |||
77 | |||
78 | #define D_SUBMODULE fw | ||
79 | #include "sdio-debug-levels.h" | ||
80 | |||
81 | /* | ||
82 | * Send a boot-mode command to the SDIO function | ||
83 | * | ||
84 | * We use a bounce buffer (i2400m->bm_cmd_buf) because we need to | ||
85 | * touch the header if the RAW flag is not set. | ||
86 | * | ||
87 | * @flags: pass thru from i2400m_bm_cmd() | ||
88 | * @return: cmd_size if ok, < 0 errno code on error. | ||
89 | * | ||
90 | * Note the command is padded to the SDIO block size for the device. | ||
91 | */ | ||
92 | ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m, | ||
93 | const struct i2400m_bootrom_header *_cmd, | ||
94 | size_t cmd_size, int flags) | ||
95 | { | ||
96 | ssize_t result; | ||
97 | struct device *dev = i2400m_dev(i2400m); | ||
98 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); | ||
99 | int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd); | ||
100 | struct i2400m_bootrom_header *cmd; | ||
101 | /* SDIO restriction */ | ||
102 | size_t cmd_size_a = ALIGN(cmd_size, I2400MS_BLK_SIZE); | ||
103 | |||
104 | d_fnstart(5, dev, "(i2400m %p cmd %p size %zu)\n", | ||
105 | i2400m, _cmd, cmd_size); | ||
106 | result = -E2BIG; | ||
107 | if (cmd_size > I2400M_BM_CMD_BUF_SIZE) | ||
108 | goto error_too_big; | ||
109 | |||
110 | memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); /* Prep command */ | ||
111 | cmd = i2400m->bm_cmd_buf; | ||
112 | if (cmd_size_a > cmd_size) /* Zero pad space */ | ||
113 | memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); | ||
114 | if ((flags & I2400M_BM_CMD_RAW) == 0) { | ||
115 | if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0)) | ||
116 | dev_warn(dev, "SW BUG: response_required == 0\n"); | ||
117 | i2400m_bm_cmd_prepare(cmd); | ||
118 | } | ||
119 | d_printf(4, dev, "BM cmd %d: %zu bytes (%zu padded)\n", | ||
120 | opcode, cmd_size, cmd_size_a); | ||
121 | d_dump(5, dev, cmd, cmd_size); | ||
122 | |||
123 | sdio_claim_host(i2400ms->func); /* Send & check */ | ||
124 | result = sdio_memcpy_toio(i2400ms->func, I2400MS_DATA_ADDR, | ||
125 | i2400m->bm_cmd_buf, cmd_size_a); | ||
126 | sdio_release_host(i2400ms->func); | ||
127 | if (result < 0) { | ||
128 | dev_err(dev, "BM cmd %d: cannot send: %ld\n", | ||
129 | opcode, (long) result); | ||
130 | goto error_cmd_send; | ||
131 | } | ||
132 | result = cmd_size; | ||
133 | error_cmd_send: | ||
134 | error_too_big: | ||
135 | d_fnend(5, dev, "(i2400m %p cmd %p size %zu) = %d\n", | ||
136 | i2400m, _cmd, cmd_size, (int) result); | ||
137 | return result; | ||
138 | } | ||
139 | |||
140 | |||
141 | /* | ||
142 | * Read an ack from the device's boot-mode (polling) | ||
143 | * | ||
144 | * @i2400m: | ||
145 | * @_ack: pointer to where to store the read data | ||
146 | * @ack_size: how many bytes we should read | ||
147 | * | ||
148 | * Returns: < 0 errno code on error; otherwise, amount of received bytes. | ||
149 | * | ||
150 | * The ACK for a BM command is always at least sizeof(*ack) bytes, so | ||
151 | * check for that. We don't need to check for device reboots | ||
152 | * | ||
153 | * NOTE: We do an artificial timeout of 1 sec over the SDIO timeout; | ||
154 | * this way we have control over it...there is no way that I know | ||
155 | * of setting an SDIO transaction timeout. | ||
156 | */ | ||
157 | ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, | ||
158 | struct i2400m_bootrom_header *ack, | ||
159 | size_t ack_size) | ||
160 | { | ||
161 | int result; | ||
162 | ssize_t rx_size; | ||
163 | u64 timeout; | ||
164 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); | ||
165 | struct sdio_func *func = i2400ms->func; | ||
166 | struct device *dev = &func->dev; | ||
167 | |||
168 | BUG_ON(sizeof(*ack) > ack_size); | ||
169 | |||
170 | d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n", | ||
171 | i2400m, ack, ack_size); | ||
172 | |||
173 | timeout = get_jiffies_64() + 2 * HZ; | ||
174 | sdio_claim_host(func); | ||
175 | while (1) { | ||
176 | if (time_after64(get_jiffies_64(), timeout)) { | ||
177 | rx_size = -ETIMEDOUT; | ||
178 | dev_err(dev, "timeout waiting for ack data\n"); | ||
179 | goto error_timedout; | ||
180 | } | ||
181 | |||
182 | /* Find the RX size, check if it fits or not -- it if | ||
183 | * doesn't fit, fail, as we have no way to dispose of | ||
184 | * the extra data. */ | ||
185 | rx_size = __i2400ms_rx_get_size(i2400ms); | ||
186 | if (rx_size < 0) | ||
187 | goto error_rx_get_size; | ||
188 | result = -ENOSPC; /* Check it fits */ | ||
189 | if (rx_size < sizeof(*ack)) { | ||
190 | rx_size = -EIO; | ||
191 | dev_err(dev, "HW BUG? received is too small (%zu vs " | ||
192 | "%zu needed)\n", sizeof(*ack), rx_size); | ||
193 | goto error_too_small; | ||
194 | } | ||
195 | if (rx_size > I2400M_BM_ACK_BUF_SIZE) { | ||
196 | dev_err(dev, "SW BUG? BM_ACK_BUF is too small (%u vs " | ||
197 | "%zu needed)\n", I2400M_BM_ACK_BUF_SIZE, | ||
198 | rx_size); | ||
199 | goto error_too_small; | ||
200 | } | ||
201 | |||
202 | /* Read it */ | ||
203 | result = sdio_memcpy_fromio(func, i2400m->bm_ack_buf, | ||
204 | I2400MS_DATA_ADDR, rx_size); | ||
205 | if (result == -ETIMEDOUT || result == -ETIME) | ||
206 | continue; | ||
207 | if (result < 0) { | ||
208 | dev_err(dev, "BM SDIO receive (%zu B) failed: %d\n", | ||
209 | rx_size, result); | ||
210 | goto error_read; | ||
211 | } else | ||
212 | break; | ||
213 | } | ||
214 | rx_size = min((ssize_t)ack_size, rx_size); | ||
215 | memcpy(ack, i2400m->bm_ack_buf, rx_size); | ||
216 | error_read: | ||
217 | error_too_small: | ||
218 | error_rx_get_size: | ||
219 | error_timedout: | ||
220 | sdio_release_host(func); | ||
221 | d_fnend(5, dev, "(i2400m %p ack %p size %zu) = %ld\n", | ||
222 | i2400m, ack, ack_size, (long) rx_size); | ||
223 | return rx_size; | ||
224 | } | ||
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c new file mode 100644 index 000000000000..a3008b904f7d --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-rx.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * SDIO RX handling | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Dirk Brandewie <dirk.j.brandewie@intel.com> | ||
37 | * - Initial implementation | ||
38 | * | ||
39 | * | ||
40 | * This handles the RX path on SDIO. | ||
41 | * | ||
42 | * The SDIO bus driver calls the "irq" routine when data is available. | ||
43 | * This is not a traditional interrupt routine since the SDIO bus | ||
44 | * driver calls us from its irq thread context. Because of this | ||
45 | * sleeping in the SDIO RX IRQ routine is okay. | ||
46 | * | ||
47 | * From there on, we obtain the size of the data that is available, | ||
48 | * allocate an skb, copy it and then pass it to the generic driver's | ||
49 | * RX routine [i2400m_rx()]. | ||
50 | * | ||
51 | * ROADMAP | ||
52 | * | ||
53 | * i2400ms_irq() | ||
54 | * i2400ms_rx() | ||
55 | * __i2400ms_rx_get_size() | ||
56 | * i2400m_rx() | ||
57 | * | ||
58 | * i2400ms_rx_setup() | ||
59 | * | ||
60 | * i2400ms_rx_release() | ||
61 | */ | ||
62 | #include <linux/workqueue.h> | ||
63 | #include <linux/wait.h> | ||
64 | #include <linux/skbuff.h> | ||
65 | #include <linux/mmc/sdio.h> | ||
66 | #include <linux/mmc/sdio_func.h> | ||
67 | #include "i2400m-sdio.h" | ||
68 | |||
69 | #define D_SUBMODULE rx | ||
70 | #include "sdio-debug-levels.h" | ||
71 | |||
72 | |||
73 | /* | ||
74 | * Read and return the amount of bytes available for RX | ||
75 | * | ||
76 | * The RX size has to be read like this: byte reads of three | ||
77 | * sequential locations; then glue'em together. | ||
78 | * | ||
79 | * sdio_readl() doesn't work. | ||
80 | */ | ||
81 | ssize_t __i2400ms_rx_get_size(struct i2400ms *i2400ms) | ||
82 | { | ||
83 | int ret, cnt, val; | ||
84 | ssize_t rx_size; | ||
85 | unsigned xfer_size_addr; | ||
86 | struct sdio_func *func = i2400ms->func; | ||
87 | struct device *dev = &i2400ms->func->dev; | ||
88 | |||
89 | d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); | ||
90 | xfer_size_addr = I2400MS_INTR_GET_SIZE_ADDR; | ||
91 | rx_size = 0; | ||
92 | for (cnt = 0; cnt < 3; cnt++) { | ||
93 | val = sdio_readb(func, xfer_size_addr + cnt, &ret); | ||
94 | if (ret < 0) { | ||
95 | dev_err(dev, "RX: Can't read byte %d of RX size from " | ||
96 | "0x%08x: %d\n", cnt, xfer_size_addr + cnt, ret); | ||
97 | rx_size = ret; | ||
98 | goto error_read; | ||
99 | } | ||
100 | rx_size = rx_size << 8 | (val & 0xff); | ||
101 | } | ||
102 | d_printf(6, dev, "RX: rx_size is %ld\n", (long) rx_size); | ||
103 | error_read: | ||
104 | d_fnend(7, dev, "(i2400ms %p) = %ld\n", i2400ms, (long) rx_size); | ||
105 | return rx_size; | ||
106 | } | ||
107 | |||
108 | |||
109 | /* | ||
110 | * Read data from the device (when in normal) | ||
111 | * | ||
112 | * Allocate an SKB of the right size, read the data in and then | ||
113 | * deliver it to the generic layer. | ||
114 | * | ||
115 | * We also check for a reboot barker. That means the device died and | ||
116 | * we have to reboot it. | ||
117 | */ | ||
118 | static | ||
119 | void i2400ms_rx(struct i2400ms *i2400ms) | ||
120 | { | ||
121 | int ret; | ||
122 | struct sdio_func *func = i2400ms->func; | ||
123 | struct device *dev = &func->dev; | ||
124 | struct i2400m *i2400m = &i2400ms->i2400m; | ||
125 | struct sk_buff *skb; | ||
126 | ssize_t rx_size; | ||
127 | |||
128 | d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); | ||
129 | rx_size = __i2400ms_rx_get_size(i2400ms); | ||
130 | if (rx_size < 0) { | ||
131 | ret = rx_size; | ||
132 | goto error_get_size; | ||
133 | } | ||
134 | ret = -ENOMEM; | ||
135 | skb = alloc_skb(rx_size, GFP_ATOMIC); | ||
136 | if (NULL == skb) { | ||
137 | dev_err(dev, "RX: unable to alloc skb\n"); | ||
138 | goto error_alloc_skb; | ||
139 | } | ||
140 | |||
141 | ret = sdio_memcpy_fromio(func, skb->data, | ||
142 | I2400MS_DATA_ADDR, rx_size); | ||
143 | if (ret < 0) { | ||
144 | dev_err(dev, "RX: SDIO data read failed: %d\n", ret); | ||
145 | goto error_memcpy_fromio; | ||
146 | } | ||
147 | /* Check if device has reset */ | ||
148 | if (!memcmp(skb->data, i2400m_NBOOT_BARKER, | ||
149 | sizeof(i2400m_NBOOT_BARKER)) | ||
150 | || !memcmp(skb->data, i2400m_SBOOT_BARKER, | ||
151 | sizeof(i2400m_SBOOT_BARKER))) { | ||
152 | ret = i2400m_dev_reset_handle(i2400m); | ||
153 | kfree_skb(skb); | ||
154 | } else { | ||
155 | skb_put(skb, rx_size); | ||
156 | i2400m_rx(i2400m, skb); | ||
157 | } | ||
158 | d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); | ||
159 | return; | ||
160 | |||
161 | error_memcpy_fromio: | ||
162 | kfree_skb(skb); | ||
163 | error_alloc_skb: | ||
164 | error_get_size: | ||
165 | d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); | ||
166 | return; | ||
167 | } | ||
168 | |||
169 | |||
170 | /* | ||
171 | * Process an interrupt from the SDIO card | ||
172 | * | ||
173 | * FIXME: need to process other events that are not just ready-to-read | ||
174 | * | ||
175 | * Checks there is data ready and then proceeds to read it. | ||
176 | */ | ||
177 | static | ||
178 | void i2400ms_irq(struct sdio_func *func) | ||
179 | { | ||
180 | int ret; | ||
181 | struct i2400ms *i2400ms = sdio_get_drvdata(func); | ||
182 | struct i2400m *i2400m = &i2400ms->i2400m; | ||
183 | struct device *dev = &func->dev; | ||
184 | int val; | ||
185 | |||
186 | d_fnstart(6, dev, "(i2400ms %p)\n", i2400ms); | ||
187 | val = sdio_readb(func, I2400MS_INTR_STATUS_ADDR, &ret); | ||
188 | if (ret < 0) { | ||
189 | dev_err(dev, "RX: Can't read interrupt status: %d\n", ret); | ||
190 | goto error_no_irq; | ||
191 | } | ||
192 | if (!val) { | ||
193 | dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n"); | ||
194 | goto error_no_irq; | ||
195 | } | ||
196 | sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); | ||
197 | if (WARN_ON(i2400m->boot_mode != 0)) | ||
198 | dev_err(dev, "RX: SW BUG? boot mode and IRQ is up?\n"); | ||
199 | else | ||
200 | i2400ms_rx(i2400ms); | ||
201 | error_no_irq: | ||
202 | d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); | ||
203 | return; | ||
204 | } | ||
205 | |||
206 | |||
207 | /* | ||
208 | * Setup SDIO RX | ||
209 | * | ||
210 | * Hooks up the IRQ handler and then enables IRQs. | ||
211 | */ | ||
212 | int i2400ms_rx_setup(struct i2400ms *i2400ms) | ||
213 | { | ||
214 | int result; | ||
215 | struct sdio_func *func = i2400ms->func; | ||
216 | struct device *dev = &func->dev; | ||
217 | |||
218 | d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); | ||
219 | sdio_claim_host(func); | ||
220 | result = sdio_claim_irq(func, i2400ms_irq); | ||
221 | if (result < 0) { | ||
222 | dev_err(dev, "Cannot claim IRQ: %d\n", result); | ||
223 | goto error_irq_claim; | ||
224 | } | ||
225 | result = 0; | ||
226 | sdio_writeb(func, 1, I2400MS_INTR_ENABLE_ADDR, &result); | ||
227 | if (result < 0) { | ||
228 | sdio_release_irq(func); | ||
229 | dev_err(dev, "Failed to enable interrupts %d\n", result); | ||
230 | } | ||
231 | error_irq_claim: | ||
232 | sdio_release_host(func); | ||
233 | d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); | ||
234 | return result; | ||
235 | } | ||
236 | |||
237 | |||
238 | /* | ||
239 | * Tear down SDIO RX | ||
240 | * | ||
241 | * Disables IRQs in the device and removes the IRQ handler. | ||
242 | */ | ||
243 | void i2400ms_rx_release(struct i2400ms *i2400ms) | ||
244 | { | ||
245 | int result; | ||
246 | struct sdio_func *func = i2400ms->func; | ||
247 | struct device *dev = &func->dev; | ||
248 | |||
249 | d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); | ||
250 | sdio_claim_host(func); | ||
251 | sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result); | ||
252 | sdio_release_irq(func); | ||
253 | sdio_release_host(func); | ||
254 | d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); | ||
255 | } | ||
diff --git a/drivers/net/wimax/i2400m/sdio-tx.c b/drivers/net/wimax/i2400m/sdio-tx.c new file mode 100644 index 000000000000..5105a5ebc44f --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-tx.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * SDIO TX transaction backends | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Dirk Brandewie <dirk.j.brandewie@intel.com> | ||
37 | * - Initial implementation | ||
38 | * | ||
39 | * | ||
40 | * Takes the TX messages in the i2400m's driver TX FIFO and sends them | ||
41 | * to the device until there are no more. | ||
42 | * | ||
43 | * If we fail sending the message, we just drop it. There isn't much | ||
44 | * we can do at this point. Most of the traffic is network, which has | ||
45 | * recovery methods for dropped packets. | ||
46 | * | ||
47 | * The SDIO functions are not atomic, so we can't run from the context | ||
48 | * where i2400m->bus_tx_kick() [i2400ms_bus_tx_kick()] is being called | ||
49 | * (some times atomic). Thus, the actual TX work is deferred to a | ||
50 | * workqueue. | ||
51 | * | ||
52 | * ROADMAP | ||
53 | * | ||
54 | * i2400ms_bus_tx_kick() | ||
55 | * i2400ms_tx_submit() [through workqueue] | ||
56 | * | ||
57 | * i2400m_tx_setup() | ||
58 | * | ||
59 | * i2400m_tx_release() | ||
60 | */ | ||
61 | #include <linux/mmc/sdio_func.h> | ||
62 | #include "i2400m-sdio.h" | ||
63 | |||
64 | #define D_SUBMODULE tx | ||
65 | #include "sdio-debug-levels.h" | ||
66 | |||
67 | |||
68 | /* | ||
69 | * Pull TX transations from the TX FIFO and send them to the device | ||
70 | * until there are no more. | ||
71 | */ | ||
72 | static | ||
73 | void i2400ms_tx_submit(struct work_struct *ws) | ||
74 | { | ||
75 | int result; | ||
76 | struct i2400ms *i2400ms = container_of(ws, struct i2400ms, tx_worker); | ||
77 | struct i2400m *i2400m = &i2400ms->i2400m; | ||
78 | struct sdio_func *func = i2400ms->func; | ||
79 | struct device *dev = &func->dev; | ||
80 | struct i2400m_msg_hdr *tx_msg; | ||
81 | size_t tx_msg_size; | ||
82 | |||
83 | d_fnstart(4, dev, "(i2400ms %p, i2400m %p)\n", i2400ms, i2400ms); | ||
84 | |||
85 | while (NULL != (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size))) { | ||
86 | d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size); | ||
87 | d_dump(5, dev, tx_msg, tx_msg_size); | ||
88 | |||
89 | sdio_claim_host(func); | ||
90 | result = sdio_memcpy_toio(func, 0, tx_msg, tx_msg_size); | ||
91 | sdio_release_host(func); | ||
92 | |||
93 | i2400m_tx_msg_sent(i2400m); | ||
94 | |||
95 | if (result < 0) { | ||
96 | dev_err(dev, "TX: cannot submit TX; tx_msg @%zu %zu B:" | ||
97 | " %d\n", (void *) tx_msg - i2400m->tx_buf, | ||
98 | tx_msg_size, result); | ||
99 | } | ||
100 | |||
101 | d_printf(2, dev, "TX: %zub submitted\n", tx_msg_size); | ||
102 | } | ||
103 | |||
104 | d_fnend(4, dev, "(i2400ms %p) = void\n", i2400ms); | ||
105 | } | ||
106 | |||
107 | |||
108 | /* | ||
109 | * The generic driver notifies us that there is data ready for TX | ||
110 | * | ||
111 | * Schedule a run of i2400ms_tx_submit() to handle it. | ||
112 | */ | ||
113 | void i2400ms_bus_tx_kick(struct i2400m *i2400m) | ||
114 | { | ||
115 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); | ||
116 | struct device *dev = &i2400ms->func->dev; | ||
117 | |||
118 | d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m); | ||
119 | |||
120 | /* schedule tx work, this is because tx may block, therefore | ||
121 | * it has to run in a thread context. | ||
122 | */ | ||
123 | queue_work(i2400ms->tx_workqueue, &i2400ms->tx_worker); | ||
124 | |||
125 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
126 | } | ||
127 | |||
128 | int i2400ms_tx_setup(struct i2400ms *i2400ms) | ||
129 | { | ||
130 | int result; | ||
131 | struct device *dev = &i2400ms->func->dev; | ||
132 | struct i2400m *i2400m = &i2400ms->i2400m; | ||
133 | |||
134 | d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); | ||
135 | |||
136 | INIT_WORK(&i2400ms->tx_worker, i2400ms_tx_submit); | ||
137 | snprintf(i2400ms->tx_wq_name, sizeof(i2400ms->tx_wq_name), | ||
138 | "%s-tx", i2400m->wimax_dev.name); | ||
139 | i2400ms->tx_workqueue = | ||
140 | create_singlethread_workqueue(i2400ms->tx_wq_name); | ||
141 | if (NULL == i2400ms->tx_workqueue) { | ||
142 | dev_err(dev, "TX: failed to create workqueue\n"); | ||
143 | result = -ENOMEM; | ||
144 | } else | ||
145 | result = 0; | ||
146 | d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); | ||
147 | return result; | ||
148 | } | ||
149 | |||
150 | void i2400ms_tx_release(struct i2400ms *i2400ms) | ||
151 | { | ||
152 | destroy_workqueue(i2400ms->tx_workqueue); | ||
153 | } | ||
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c new file mode 100644 index 000000000000..1bfa283bbd8a --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio.c | |||
@@ -0,0 +1,511 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Linux driver model glue for the SDIO device, reset & fw upload | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Dirk Brandewie <dirk.j.brandewie@intel.com> | ||
8 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
9 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License version | ||
13 | * 2 as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
23 | * 02110-1301, USA. | ||
24 | * | ||
25 | * | ||
26 | * See i2400m-sdio.h for a general description of this driver. | ||
27 | * | ||
28 | * This file implements driver model glue, and hook ups for the | ||
29 | * generic driver to implement the bus-specific functions (device | ||
30 | * communication setup/tear down, firmware upload and resetting). | ||
31 | * | ||
32 | * ROADMAP | ||
33 | * | ||
34 | * i2400m_probe() | ||
35 | * alloc_netdev() | ||
36 | * i2400ms_netdev_setup() | ||
37 | * i2400ms_init() | ||
38 | * i2400m_netdev_setup() | ||
39 | * i2400ms_enable_function() | ||
40 | * i2400m_setup() | ||
41 | * | ||
42 | * i2400m_remove() | ||
43 | * i2400m_release() | ||
44 | * free_netdev(net_dev) | ||
45 | * | ||
46 | * i2400ms_bus_reset() Called by i2400m->bus_reset | ||
47 | * __i2400ms_reset() | ||
48 | * __i2400ms_send_barker() | ||
49 | * | ||
50 | * i2400ms_bus_dev_start() Called by i2400m_dev_start() [who is | ||
51 | * i2400ms_tx_setup() called by i2400m_setup()] | ||
52 | * i2400ms_rx_setup() | ||
53 | * | ||
54 | * i2400ms_bus_dev_stop() Called by i2400m_dev_stop() [who is | ||
55 | * i2400ms_rx_release() is called by i2400m_release()] | ||
56 | * i2400ms_tx_release() | ||
57 | * | ||
58 | */ | ||
59 | |||
60 | #include <linux/debugfs.h> | ||
61 | #include <linux/mmc/sdio.h> | ||
62 | #include <linux/mmc/sdio_func.h> | ||
63 | #include "i2400m-sdio.h" | ||
64 | #include <linux/wimax/i2400m.h> | ||
65 | |||
66 | #define D_SUBMODULE main | ||
67 | #include "sdio-debug-levels.h" | ||
68 | |||
69 | /* IOE WiMAX function timeout in seconds */ | ||
70 | static int ioe_timeout = 2; | ||
71 | module_param(ioe_timeout, int, 0); | ||
72 | |||
73 | /* Our firmware file name */ | ||
74 | #define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-" I2400M_FW_VERSION ".sbcf" | ||
75 | |||
76 | /* | ||
77 | * Enable the SDIO function | ||
78 | * | ||
79 | * Tries to enable the SDIO function; might fail if it is still not | ||
80 | * ready (in some hardware, the SDIO WiMAX function is only enabled | ||
81 | * when we ask it to explicitly doing). Tries until a timeout is | ||
82 | * reached. | ||
83 | * | ||
84 | * The reverse of this is...sdio_disable_function() | ||
85 | * | ||
86 | * Returns: 0 if the SDIO function was enabled, < 0 errno code on | ||
87 | * error (-ENODEV when it was unable to enable the function). | ||
88 | */ | ||
89 | static | ||
90 | int i2400ms_enable_function(struct sdio_func *func) | ||
91 | { | ||
92 | u64 timeout; | ||
93 | int err; | ||
94 | struct device *dev = &func->dev; | ||
95 | |||
96 | d_fnstart(3, dev, "(func %p)\n", func); | ||
97 | /* Setup timeout (FIXME: This needs to read the CIS table to | ||
98 | * get a real timeout) and then wait for the device to signal | ||
99 | * it is ready */ | ||
100 | timeout = get_jiffies_64() + ioe_timeout * HZ; | ||
101 | err = -ENODEV; | ||
102 | while (err != 0 && time_before64(get_jiffies_64(), timeout)) { | ||
103 | sdio_claim_host(func); | ||
104 | err = sdio_enable_func(func); | ||
105 | if (0 == err) { | ||
106 | sdio_release_host(func); | ||
107 | d_printf(2, dev, "SDIO function enabled\n"); | ||
108 | goto function_enabled; | ||
109 | } | ||
110 | d_printf(2, dev, "SDIO function failed to enable: %d\n", err); | ||
111 | sdio_disable_func(func); | ||
112 | sdio_release_host(func); | ||
113 | msleep(I2400MS_INIT_SLEEP_INTERVAL); | ||
114 | } | ||
115 | /* If timed out, device is not there yet -- get -ENODEV so | ||
116 | * the device driver core will retry later on. */ | ||
117 | if (err == -ETIME) { | ||
118 | dev_err(dev, "Can't enable WiMAX function; " | ||
119 | " has the function been enabled?\n"); | ||
120 | err = -ENODEV; | ||
121 | } | ||
122 | function_enabled: | ||
123 | d_fnend(3, dev, "(func %p) = %d\n", func, err); | ||
124 | return err; | ||
125 | } | ||
126 | |||
127 | |||
128 | /* | ||
129 | * Setup driver resources needed to communicate with the device | ||
130 | * | ||
131 | * The fw needs some time to settle, and it was just uploaded, | ||
132 | * so give it a break first. I'd prefer to just wait for the device to | ||
133 | * send something, but seems the poking we do to enable SDIO stuff | ||
134 | * interferes with it, so just give it a break before starting... | ||
135 | */ | ||
136 | static | ||
137 | int i2400ms_bus_dev_start(struct i2400m *i2400m) | ||
138 | { | ||
139 | int result; | ||
140 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); | ||
141 | struct sdio_func *func = i2400ms->func; | ||
142 | struct device *dev = &func->dev; | ||
143 | |||
144 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
145 | msleep(200); | ||
146 | result = i2400ms_rx_setup(i2400ms); | ||
147 | if (result < 0) | ||
148 | goto error_rx_setup; | ||
149 | result = i2400ms_tx_setup(i2400ms); | ||
150 | if (result < 0) | ||
151 | goto error_tx_setup; | ||
152 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
153 | return result; | ||
154 | |||
155 | i2400ms_tx_release(i2400ms); | ||
156 | error_tx_setup: | ||
157 | i2400ms_rx_release(i2400ms); | ||
158 | error_rx_setup: | ||
159 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
160 | return result; | ||
161 | } | ||
162 | |||
163 | |||
164 | static | ||
165 | void i2400ms_bus_dev_stop(struct i2400m *i2400m) | ||
166 | { | ||
167 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); | ||
168 | struct sdio_func *func = i2400ms->func; | ||
169 | struct device *dev = &func->dev; | ||
170 | |||
171 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
172 | i2400ms_rx_release(i2400ms); | ||
173 | i2400ms_tx_release(i2400ms); | ||
174 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
175 | } | ||
176 | |||
177 | |||
178 | /* | ||
179 | * Sends a barker buffer to the device | ||
180 | * | ||
181 | * This helper will allocate a kmalloced buffer and use it to transmit | ||
182 | * (then free it). Reason for this is that the SDIO host controller | ||
183 | * expects alignment (unknown exactly which) which the stack won't | ||
184 | * really provide and certain arches/host-controller combinations | ||
185 | * cannot use stack/vmalloc/text areas for DMA transfers. | ||
186 | */ | ||
187 | static | ||
188 | int __i2400ms_send_barker(struct i2400ms *i2400ms, | ||
189 | const __le32 *barker, size_t barker_size) | ||
190 | { | ||
191 | int ret; | ||
192 | struct sdio_func *func = i2400ms->func; | ||
193 | struct device *dev = &func->dev; | ||
194 | void *buffer; | ||
195 | |||
196 | ret = -ENOMEM; | ||
197 | buffer = kmalloc(I2400MS_BLK_SIZE, GFP_KERNEL); | ||
198 | if (buffer == NULL) | ||
199 | goto error_kzalloc; | ||
200 | |||
201 | memcpy(buffer, barker, barker_size); | ||
202 | sdio_claim_host(func); | ||
203 | ret = sdio_memcpy_toio(func, 0, buffer, I2400MS_BLK_SIZE); | ||
204 | sdio_release_host(func); | ||
205 | |||
206 | if (ret < 0) | ||
207 | d_printf(0, dev, "E: barker error: %d\n", ret); | ||
208 | |||
209 | kfree(buffer); | ||
210 | error_kzalloc: | ||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | |||
215 | /* | ||
216 | * Reset a device at different levels (warm, cold or bus) | ||
217 | * | ||
218 | * @i2400ms: device descriptor | ||
219 | * @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS) | ||
220 | * | ||
221 | * FIXME: not tested -- need to confirm expected effects | ||
222 | * | ||
223 | * Warm and cold resets get an SDIO reset if they fail (unimplemented) | ||
224 | * | ||
225 | * Warm reset: | ||
226 | * | ||
227 | * The device will be fully reset internally, but won't be | ||
228 | * disconnected from the USB bus (so no reenumeration will | ||
229 | * happen). Firmware upload will be neccessary. | ||
230 | * | ||
231 | * The device will send a reboot barker in the notification endpoint | ||
232 | * that will trigger the driver to reinitialize the state | ||
233 | * automatically from notif.c:i2400m_notification_grok() into | ||
234 | * i2400m_dev_bootstrap_delayed(). | ||
235 | * | ||
236 | * Cold and bus (USB) reset: | ||
237 | * | ||
238 | * The device will be fully reset internally, disconnected from the | ||
239 | * USB bus an a reenumeration will happen. Firmware upload will be | ||
240 | * neccessary. Thus, we don't do any locking or struct | ||
241 | * reinitialization, as we are going to be fully disconnected and | ||
242 | * reenumerated. | ||
243 | * | ||
244 | * Note we need to return -ENODEV if a warm reset was requested and we | ||
245 | * had to resort to a bus reset. See i2400m_op_reset(), wimax_reset() | ||
246 | * and wimax_dev->op_reset. | ||
247 | * | ||
248 | * WARNING: no driver state saved/fixed | ||
249 | */ | ||
250 | static | ||
251 | int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) | ||
252 | { | ||
253 | int result; | ||
254 | struct i2400ms *i2400ms = | ||
255 | container_of(i2400m, struct i2400ms, i2400m); | ||
256 | struct device *dev = i2400m_dev(i2400m); | ||
257 | static const __le32 i2400m_WARM_BOOT_BARKER[4] = { | ||
258 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
259 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
260 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
261 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
262 | }; | ||
263 | static const __le32 i2400m_COLD_BOOT_BARKER[4] = { | ||
264 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
265 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
266 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
267 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
268 | }; | ||
269 | |||
270 | if (rt == I2400M_RT_WARM) | ||
271 | result = __i2400ms_send_barker(i2400ms, i2400m_WARM_BOOT_BARKER, | ||
272 | sizeof(i2400m_WARM_BOOT_BARKER)); | ||
273 | else if (rt == I2400M_RT_COLD) | ||
274 | result = __i2400ms_send_barker(i2400ms, i2400m_COLD_BOOT_BARKER, | ||
275 | sizeof(i2400m_COLD_BOOT_BARKER)); | ||
276 | else if (rt == I2400M_RT_BUS) { | ||
277 | do_bus_reset: | ||
278 | dev_err(dev, "FIXME: SDIO bus reset not implemented\n"); | ||
279 | result = rt == I2400M_RT_WARM ? -ENODEV : -ENOSYS; | ||
280 | } else | ||
281 | BUG(); | ||
282 | if (result < 0 && rt != I2400M_RT_BUS) { | ||
283 | dev_err(dev, "%s reset failed (%d); trying SDIO reset\n", | ||
284 | rt == I2400M_RT_WARM ? "warm" : "cold", result); | ||
285 | rt = I2400M_RT_BUS; | ||
286 | goto do_bus_reset; | ||
287 | } | ||
288 | return result; | ||
289 | } | ||
290 | |||
291 | |||
292 | static | ||
293 | void i2400ms_netdev_setup(struct net_device *net_dev) | ||
294 | { | ||
295 | struct i2400m *i2400m = net_dev_to_i2400m(net_dev); | ||
296 | struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); | ||
297 | i2400ms_init(i2400ms); | ||
298 | i2400m_netdev_setup(net_dev); | ||
299 | } | ||
300 | |||
301 | |||
302 | /* | ||
303 | * Debug levels control; see debug.h | ||
304 | */ | ||
305 | struct d_level D_LEVEL[] = { | ||
306 | D_SUBMODULE_DEFINE(main), | ||
307 | D_SUBMODULE_DEFINE(tx), | ||
308 | D_SUBMODULE_DEFINE(rx), | ||
309 | D_SUBMODULE_DEFINE(fw), | ||
310 | }; | ||
311 | size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); | ||
312 | |||
313 | |||
314 | #define __debugfs_register(prefix, name, parent) \ | ||
315 | do { \ | ||
316 | result = d_level_register_debugfs(prefix, name, parent); \ | ||
317 | if (result < 0) \ | ||
318 | goto error; \ | ||
319 | } while (0) | ||
320 | |||
321 | |||
322 | static | ||
323 | int i2400ms_debugfs_add(struct i2400ms *i2400ms) | ||
324 | { | ||
325 | int result; | ||
326 | struct dentry *dentry = i2400ms->i2400m.wimax_dev.debugfs_dentry; | ||
327 | |||
328 | dentry = debugfs_create_dir("i2400m-usb", dentry); | ||
329 | result = PTR_ERR(dentry); | ||
330 | if (IS_ERR(dentry)) { | ||
331 | if (result == -ENODEV) | ||
332 | result = 0; /* No debugfs support */ | ||
333 | goto error; | ||
334 | } | ||
335 | i2400ms->debugfs_dentry = dentry; | ||
336 | __debugfs_register("dl_", main, dentry); | ||
337 | __debugfs_register("dl_", tx, dentry); | ||
338 | __debugfs_register("dl_", rx, dentry); | ||
339 | __debugfs_register("dl_", fw, dentry); | ||
340 | |||
341 | return 0; | ||
342 | |||
343 | error: | ||
344 | debugfs_remove_recursive(i2400ms->debugfs_dentry); | ||
345 | return result; | ||
346 | } | ||
347 | |||
348 | |||
349 | /* | ||
350 | * Probe a i2400m interface and register it | ||
351 | * | ||
352 | * @func: SDIO function | ||
353 | * @id: SDIO device ID | ||
354 | * @returns: 0 if ok, < 0 errno code on error. | ||
355 | * | ||
356 | * Alloc a net device, initialize the bus-specific details and then | ||
357 | * calls the bus-generic initialization routine. That will register | ||
358 | * the wimax and netdev devices, upload the firmware [using | ||
359 | * _bus_bm_*()], call _bus_dev_start() to finalize the setup of the | ||
360 | * communication with the device and then will start to talk to it to | ||
361 | * finnish setting it up. | ||
362 | * | ||
363 | * Initialization is tricky; some instances of the hw are packed with | ||
364 | * others in a way that requires a third driver that enables the WiMAX | ||
365 | * function. In those cases, we can't enable the SDIO function and | ||
366 | * we'll return with -ENODEV. When the driver that enables the WiMAX | ||
367 | * function does its thing, it has to do a bus_rescan_devices() on the | ||
368 | * SDIO bus so this driver is called again to enumerate the WiMAX | ||
369 | * function. | ||
370 | */ | ||
371 | static | ||
372 | int i2400ms_probe(struct sdio_func *func, | ||
373 | const struct sdio_device_id *id) | ||
374 | { | ||
375 | int result; | ||
376 | struct net_device *net_dev; | ||
377 | struct device *dev = &func->dev; | ||
378 | struct i2400m *i2400m; | ||
379 | struct i2400ms *i2400ms; | ||
380 | |||
381 | /* Allocate instance [calls i2400m_netdev_setup() on it]. */ | ||
382 | result = -ENOMEM; | ||
383 | net_dev = alloc_netdev(sizeof(*i2400ms), "wmx%d", | ||
384 | i2400ms_netdev_setup); | ||
385 | if (net_dev == NULL) { | ||
386 | dev_err(dev, "no memory for network device instance\n"); | ||
387 | goto error_alloc_netdev; | ||
388 | } | ||
389 | SET_NETDEV_DEV(net_dev, dev); | ||
390 | i2400m = net_dev_to_i2400m(net_dev); | ||
391 | i2400ms = container_of(i2400m, struct i2400ms, i2400m); | ||
392 | i2400m->wimax_dev.net_dev = net_dev; | ||
393 | i2400ms->func = func; | ||
394 | sdio_set_drvdata(func, i2400ms); | ||
395 | |||
396 | i2400m->bus_tx_block_size = I2400MS_BLK_SIZE; | ||
397 | i2400m->bus_pl_size_max = I2400MS_PL_SIZE_MAX; | ||
398 | i2400m->bus_dev_start = i2400ms_bus_dev_start; | ||
399 | i2400m->bus_dev_stop = i2400ms_bus_dev_stop; | ||
400 | i2400m->bus_tx_kick = i2400ms_bus_tx_kick; | ||
401 | i2400m->bus_reset = i2400ms_bus_reset; | ||
402 | i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send; | ||
403 | i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack; | ||
404 | i2400m->bus_fw_name = I2400MS_FW_FILE_NAME; | ||
405 | i2400m->bus_bm_mac_addr_impaired = 1; | ||
406 | |||
407 | result = i2400ms_enable_function(i2400ms->func); | ||
408 | if (result < 0) { | ||
409 | dev_err(dev, "Cannot enable SDIO function: %d\n", result); | ||
410 | goto error_func_enable; | ||
411 | } | ||
412 | |||
413 | sdio_claim_host(func); | ||
414 | result = sdio_set_block_size(func, I2400MS_BLK_SIZE); | ||
415 | if (result < 0) { | ||
416 | dev_err(dev, "Failed to set block size: %d\n", result); | ||
417 | goto error_set_blk_size; | ||
418 | } | ||
419 | sdio_release_host(func); | ||
420 | |||
421 | result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT); | ||
422 | if (result < 0) { | ||
423 | dev_err(dev, "cannot setup device: %d\n", result); | ||
424 | goto error_setup; | ||
425 | } | ||
426 | |||
427 | result = i2400ms_debugfs_add(i2400ms); | ||
428 | if (result < 0) { | ||
429 | dev_err(dev, "cannot create SDIO debugfs: %d\n", | ||
430 | result); | ||
431 | goto error_debugfs_add; | ||
432 | } | ||
433 | return 0; | ||
434 | |||
435 | error_debugfs_add: | ||
436 | i2400m_release(i2400m); | ||
437 | error_setup: | ||
438 | sdio_set_drvdata(func, NULL); | ||
439 | sdio_claim_host(func); | ||
440 | error_set_blk_size: | ||
441 | sdio_disable_func(func); | ||
442 | sdio_release_host(func); | ||
443 | error_func_enable: | ||
444 | free_netdev(net_dev); | ||
445 | error_alloc_netdev: | ||
446 | return result; | ||
447 | } | ||
448 | |||
449 | |||
450 | static | ||
451 | void i2400ms_remove(struct sdio_func *func) | ||
452 | { | ||
453 | struct device *dev = &func->dev; | ||
454 | struct i2400ms *i2400ms = sdio_get_drvdata(func); | ||
455 | struct i2400m *i2400m = &i2400ms->i2400m; | ||
456 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
457 | |||
458 | d_fnstart(3, dev, "SDIO func %p\n", func); | ||
459 | debugfs_remove_recursive(i2400ms->debugfs_dentry); | ||
460 | i2400m_release(i2400m); | ||
461 | sdio_set_drvdata(func, NULL); | ||
462 | sdio_claim_host(func); | ||
463 | sdio_disable_func(func); | ||
464 | sdio_release_host(func); | ||
465 | free_netdev(net_dev); | ||
466 | d_fnend(3, dev, "SDIO func %p\n", func); | ||
467 | } | ||
468 | |||
469 | enum { | ||
470 | I2400MS_INTEL_VID = 0x89, | ||
471 | }; | ||
472 | |||
473 | static | ||
474 | const struct sdio_device_id i2400ms_sdio_ids[] = { | ||
475 | /* Intel: i2400m WiMAX over SDIO */ | ||
476 | { SDIO_DEVICE(I2400MS_INTEL_VID, 0x1402) }, | ||
477 | { }, /* end: all zeroes */ | ||
478 | }; | ||
479 | MODULE_DEVICE_TABLE(sdio, i2400ms_sdio_ids); | ||
480 | |||
481 | |||
482 | static | ||
483 | struct sdio_driver i2400m_sdio_driver = { | ||
484 | .name = KBUILD_MODNAME, | ||
485 | .probe = i2400ms_probe, | ||
486 | .remove = i2400ms_remove, | ||
487 | .id_table = i2400ms_sdio_ids, | ||
488 | }; | ||
489 | |||
490 | |||
491 | static | ||
492 | int __init i2400ms_driver_init(void) | ||
493 | { | ||
494 | return sdio_register_driver(&i2400m_sdio_driver); | ||
495 | } | ||
496 | module_init(i2400ms_driver_init); | ||
497 | |||
498 | |||
499 | static | ||
500 | void __exit i2400ms_driver_exit(void) | ||
501 | { | ||
502 | flush_scheduled_work(); /* for the stuff we schedule */ | ||
503 | sdio_unregister_driver(&i2400m_sdio_driver); | ||
504 | } | ||
505 | module_exit(i2400ms_driver_exit); | ||
506 | |||
507 | |||
508 | MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); | ||
509 | MODULE_DESCRIPTION("Intel 2400M WiMAX networking for SDIO"); | ||
510 | MODULE_LICENSE("GPL"); | ||
511 | MODULE_FIRMWARE(I2400MS_FW_FILE_NAME); | ||
diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c new file mode 100644 index 000000000000..613a88ffd651 --- /dev/null +++ b/drivers/net/wimax/i2400m/tx.c | |||
@@ -0,0 +1,817 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Generic (non-bus specific) TX handling | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * - Initial implementation | ||
38 | * | ||
39 | * Intel Corporation <linux-wimax@intel.com> | ||
40 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
41 | * - Rewritten to use a single FIFO to lower the memory allocation | ||
42 | * pressure and optimize cache hits when copying to the queue, as | ||
43 | * well as splitting out bus-specific code. | ||
44 | * | ||
45 | * | ||
46 | * Implements data transmission to the device; this is done through a | ||
47 | * software FIFO, as data/control frames can be coalesced (while the | ||
48 | * device is reading the previous tx transaction, others accumulate). | ||
49 | * | ||
50 | * A FIFO is used because at the end it is resource-cheaper that trying | ||
51 | * to implement scatter/gather over USB. As well, most traffic is going | ||
52 | * to be download (vs upload). | ||
53 | * | ||
54 | * The format for sending/receiving data to/from the i2400m is | ||
55 | * described in detail in rx.c:PROTOCOL FORMAT. In here we implement | ||
56 | * the transmission of that. This is split between a bus-independent | ||
57 | * part that just prepares everything and a bus-specific part that | ||
58 | * does the actual transmission over the bus to the device (in the | ||
59 | * bus-specific driver). | ||
60 | * | ||
61 | * | ||
62 | * The general format of a device-host transaction is MSG-HDR, PLD1, | ||
63 | * PLD2...PLDN, PL1, PL2,...PLN, PADDING. | ||
64 | * | ||
65 | * Because we need the send payload descriptors and then payloads and | ||
66 | * because it is kind of expensive to do scatterlists in USB (one URB | ||
67 | * per node), it becomes cheaper to append all the data to a FIFO | ||
68 | * (copying to a FIFO potentially in cache is cheaper). | ||
69 | * | ||
70 | * Then the bus-specific code takes the parts of that FIFO that are | ||
71 | * written and passes them to the device. | ||
72 | * | ||
73 | * So the concepts to keep in mind there are: | ||
74 | * | ||
75 | * We use a FIFO to queue the data in a linear buffer. We first append | ||
76 | * a MSG-HDR, space for I2400M_TX_PLD_MAX payload descriptors and then | ||
77 | * go appending payloads until we run out of space or of payload | ||
78 | * descriptors. Then we append padding to make the whole transaction a | ||
79 | * multiple of i2400m->bus_tx_block_size (as defined by the bus layer). | ||
80 | * | ||
81 | * - A TX message: a combination of a message header, payload | ||
82 | * descriptors and payloads. | ||
83 | * | ||
84 | * Open: it is marked as active (i2400m->tx_msg is valid) and we | ||
85 | * can keep adding payloads to it. | ||
86 | * | ||
87 | * Closed: we are not appending more payloads to this TX message | ||
88 | * (exahusted space in the queue, too many payloads or | ||
89 | * whichever). We have appended padding so the whole message | ||
90 | * length is aligned to i2400m->bus_tx_block_size (as set by the | ||
91 | * bus/transport layer). | ||
92 | * | ||
93 | * - Most of the time we keep a TX message open to which we append | ||
94 | * payloads. | ||
95 | * | ||
96 | * - If we are going to append and there is no more space (we are at | ||
97 | * the end of the FIFO), we close the message, mark the rest of the | ||
98 | * FIFO space unusable (skip_tail), create a new message at the | ||
99 | * beginning of the FIFO (if there is space) and append the message | ||
100 | * there. | ||
101 | * | ||
102 | * This is because we need to give linear TX messages to the bus | ||
103 | * engine. So we don't write a message to the remaining FIFO space | ||
104 | * until the tail and continue at the head of it. | ||
105 | * | ||
106 | * - We overload one of the fields in the message header to use it as | ||
107 | * 'size' of the TX message, so we can iterate over them. It also | ||
108 | * contains a flag that indicates if we have to skip it or not. | ||
109 | * When we send the buffer, we update that to its real on-the-wire | ||
110 | * value. | ||
111 | * | ||
112 | * - The MSG-HDR PLD1...PLD2 stuff has to be a size multiple of 16. | ||
113 | * | ||
114 | * It follows that if MSG-HDR says we have N messages, the whole | ||
115 | * header + descriptors is 16 + 4*N; for those to be a multiple of | ||
116 | * 16, it follows that N can be 4, 8, 12, ... (32, 48, 64, 80... | ||
117 | * bytes). | ||
118 | * | ||
119 | * So if we have only 1 payload, we have to submit a header that in | ||
120 | * all truth has space for 4. | ||
121 | * | ||
122 | * The implication is that we reserve space for 12 (64 bytes); but | ||
123 | * if we fill up only (eg) 2, our header becomes 32 bytes only. So | ||
124 | * the TX engine has to shift those 32 bytes of msg header and 2 | ||
125 | * payloads and padding so that right after it the payloads start | ||
126 | * and the TX engine has to know about that. | ||
127 | * | ||
128 | * It is cheaper to move the header up than the whole payloads down. | ||
129 | * | ||
130 | * We do this in i2400m_tx_close(). See 'i2400m_msg_hdr->offset'. | ||
131 | * | ||
132 | * - Each payload has to be size-padded to 16 bytes; before appending | ||
133 | * it, we just do it. | ||
134 | * | ||
135 | * - The whole message has to be padded to i2400m->bus_tx_block_size; | ||
136 | * we do this at close time. Thus, when reserving space for the | ||
137 | * payload, we always make sure there is also free space for this | ||
138 | * padding that sooner or later will happen. | ||
139 | * | ||
140 | * When we append a message, we tell the bus specific code to kick in | ||
141 | * TXs. It will TX (in parallel) until the buffer is exhausted--hence | ||
142 | * the lockin we do. The TX code will only send a TX message at the | ||
143 | * time (which remember, might contain more than one payload). Of | ||
144 | * course, when the bus-specific driver attempts to TX a message that | ||
145 | * is still open, it gets closed first. | ||
146 | * | ||
147 | * Gee, this is messy; well a picture. In the example below we have a | ||
148 | * partially full FIFO, with a closed message ready to be delivered | ||
149 | * (with a moved message header to make sure it is size-aligned to | ||
150 | * 16), TAIL room that was unusable (and thus is marked with a message | ||
151 | * header that says 'skip this') and at the head of the buffer, an | ||
152 | * imcomplete message with a couple of payloads. | ||
153 | * | ||
154 | * N ___________________________________________________ | ||
155 | * | | | ||
156 | * | TAIL room | | ||
157 | * | | | ||
158 | * | msg_hdr to skip (size |= 0x80000) | | ||
159 | * |---------------------------------------------------|------- | ||
160 | * | | /|\ | ||
161 | * | | | | ||
162 | * | TX message padding | | | ||
163 | * | | | | ||
164 | * | | | | ||
165 | * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | | ||
166 | * | | | | ||
167 | * | payload 1 | | | ||
168 | * | | N * tx_block_size | ||
169 | * | | | | ||
170 | * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | | ||
171 | * | | | | ||
172 | * | payload 1 | | | ||
173 | * | | | | ||
174 | * | | | | ||
175 | * |- - - - - - - - - - - - - - - - - - - - - - - - - -|- -|- - - - | ||
176 | * | padding 3 /|\ | | /|\ | ||
177 | * | padding 2 | | | | | ||
178 | * | pld 1 32 bytes (2 * 16) | | | | ||
179 | * | pld 0 | | | | | ||
180 | * | moved msg_hdr \|/ | \|/ | | ||
181 | * |- - - - - - - - - - - - - - - - - - - - - - - - - -|- - - | | ||
182 | * | | _PLD_SIZE | ||
183 | * | unused | | | ||
184 | * | | | | ||
185 | * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | | ||
186 | * | msg_hdr (size X) [this message is closed] | \|/ | ||
187 | * |===================================================|========== <=== OUT | ||
188 | * | | | ||
189 | * | | | ||
190 | * | | | ||
191 | * | Free rooom | | ||
192 | * | | | ||
193 | * | | | ||
194 | * | | | ||
195 | * | | | ||
196 | * | | | ||
197 | * | | | ||
198 | * | | | ||
199 | * | | | ||
200 | * | | | ||
201 | * |===================================================|========== <=== IN | ||
202 | * | | | ||
203 | * | | | ||
204 | * | | | ||
205 | * | | | ||
206 | * | payload 1 | | ||
207 | * | | | ||
208 | * | | | ||
209 | * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | ||
210 | * | | | ||
211 | * | payload 0 | | ||
212 | * | | | ||
213 | * | | | ||
214 | * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | ||
215 | * | pld 11 /|\ | | ||
216 | * | ... | | | ||
217 | * | pld 1 64 bytes (2 * 16) | | ||
218 | * | pld 0 | | | ||
219 | * | msg_hdr (size X) \|/ [message is open] | | ||
220 | * 0 --------------------------------------------------- | ||
221 | * | ||
222 | * | ||
223 | * ROADMAP | ||
224 | * | ||
225 | * i2400m_tx_setup() Called by i2400m_setup | ||
226 | * i2400m_tx_release() Called by i2400m_release() | ||
227 | * | ||
228 | * i2400m_tx() Called to send data or control frames | ||
229 | * i2400m_tx_fifo_push() Allocates append-space in the FIFO | ||
230 | * i2400m_tx_new() Opens a new message in the FIFO | ||
231 | * i2400m_tx_fits() Checks if a new payload fits in the message | ||
232 | * i2400m_tx_close() Closes an open message in the FIFO | ||
233 | * i2400m_tx_skip_tail() Marks unusable FIFO tail space | ||
234 | * i2400m->bus_tx_kick() | ||
235 | * | ||
236 | * Now i2400m->bus_tx_kick() is the the bus-specific driver backend | ||
237 | * implementation; that would do: | ||
238 | * | ||
239 | * i2400m->bus_tx_kick() | ||
240 | * i2400m_tx_msg_get() Gets first message ready to go | ||
241 | * ...sends it... | ||
242 | * i2400m_tx_msg_sent() Ack the message is sent; repeat from | ||
243 | * _tx_msg_get() until it returns NULL | ||
244 | * (FIFO empty). | ||
245 | */ | ||
246 | #include <linux/netdevice.h> | ||
247 | #include "i2400m.h" | ||
248 | |||
249 | |||
250 | #define D_SUBMODULE tx | ||
251 | #include "debug-levels.h" | ||
252 | |||
253 | enum { | ||
254 | /** | ||
255 | * TX Buffer size | ||
256 | * | ||
257 | * Doc says maximum transaction is 16KiB. If we had 16KiB en | ||
258 | * route and 16KiB being queued, it boils down to needing | ||
259 | * 32KiB. | ||
260 | */ | ||
261 | I2400M_TX_BUF_SIZE = 32768, | ||
262 | /** | ||
263 | * Message header and payload descriptors have to be 16 | ||
264 | * aligned (16 + 4 * N = 16 * M). If we take that average sent | ||
265 | * packets are MTU size (~1400-~1500) it follows that we could | ||
266 | * fit at most 10-11 payloads in one transaction. To meet the | ||
267 | * alignment requirement, that means we need to leave space | ||
268 | * for 12 (64 bytes). To simplify, we leave space for that. If | ||
269 | * at the end there are less, we pad up to the nearest | ||
270 | * multiple of 16. | ||
271 | */ | ||
272 | I2400M_TX_PLD_MAX = 12, | ||
273 | I2400M_TX_PLD_SIZE = sizeof(struct i2400m_msg_hdr) | ||
274 | + I2400M_TX_PLD_MAX * sizeof(struct i2400m_pld), | ||
275 | I2400M_TX_SKIP = 0x80000000, | ||
276 | }; | ||
277 | |||
278 | #define TAIL_FULL ((void *)~(unsigned long)NULL) | ||
279 | |||
280 | /* | ||
281 | * Allocate @size bytes in the TX fifo, return a pointer to it | ||
282 | * | ||
283 | * @i2400m: device descriptor | ||
284 | * @size: size of the buffer we need to allocate | ||
285 | * @padding: ensure that there is at least this many bytes of free | ||
286 | * contiguous space in the fifo. This is needed because later on | ||
287 | * we might need to add padding. | ||
288 | * | ||
289 | * Returns: | ||
290 | * | ||
291 | * Pointer to the allocated space. NULL if there is no | ||
292 | * space. TAIL_FULL if there is no space at the tail but there is at | ||
293 | * the head (Case B below). | ||
294 | * | ||
295 | * These are the two basic cases we need to keep an eye for -- it is | ||
296 | * much better explained in linux/kernel/kfifo.c, but this code | ||
297 | * basically does the same. No rocket science here. | ||
298 | * | ||
299 | * Case A Case B | ||
300 | * N ___________ ___________ | ||
301 | * | tail room | | data | | ||
302 | * | | | | | ||
303 | * |<- IN ->| |<- OUT ->| | ||
304 | * | | | | | ||
305 | * | data | | room | | ||
306 | * | | | | | ||
307 | * |<- OUT ->| |<- IN ->| | ||
308 | * | | | | | ||
309 | * | head room | | data | | ||
310 | * 0 ----------- ----------- | ||
311 | * | ||
312 | * We allocate only *contiguous* space. | ||
313 | * | ||
314 | * We can allocate only from 'room'. In Case B, it is simple; in case | ||
315 | * A, we only try from the tail room; if it is not enough, we just | ||
316 | * fail and return TAIL_FULL and let the caller figure out if we wants to | ||
317 | * skip the tail room and try to allocate from the head. | ||
318 | * | ||
319 | * Note: | ||
320 | * | ||
321 | * Assumes i2400m->tx_lock is taken, and we use that as a barrier | ||
322 | * | ||
323 | * The indexes keep increasing and we reset them to zero when we | ||
324 | * pop data off the queue | ||
325 | */ | ||
326 | static | ||
327 | void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) | ||
328 | { | ||
329 | struct device *dev = i2400m_dev(i2400m); | ||
330 | size_t room, tail_room, needed_size; | ||
331 | void *ptr; | ||
332 | |||
333 | needed_size = size + padding; | ||
334 | room = I2400M_TX_BUF_SIZE - (i2400m->tx_in - i2400m->tx_out); | ||
335 | if (room < needed_size) { /* this takes care of Case B */ | ||
336 | d_printf(2, dev, "fifo push %zu/%zu: no space\n", | ||
337 | size, padding); | ||
338 | return NULL; | ||
339 | } | ||
340 | /* Is there space at the tail? */ | ||
341 | tail_room = I2400M_TX_BUF_SIZE - i2400m->tx_in % I2400M_TX_BUF_SIZE; | ||
342 | if (tail_room < needed_size) { | ||
343 | if (i2400m->tx_out % I2400M_TX_BUF_SIZE | ||
344 | < i2400m->tx_in % I2400M_TX_BUF_SIZE) { | ||
345 | d_printf(2, dev, "fifo push %zu/%zu: tail full\n", | ||
346 | size, padding); | ||
347 | return TAIL_FULL; /* There might be head space */ | ||
348 | } else { | ||
349 | d_printf(2, dev, "fifo push %zu/%zu: no head space\n", | ||
350 | size, padding); | ||
351 | return NULL; /* There is no space */ | ||
352 | } | ||
353 | } | ||
354 | ptr = i2400m->tx_buf + i2400m->tx_in % I2400M_TX_BUF_SIZE; | ||
355 | d_printf(2, dev, "fifo push %zu/%zu: at @%zu\n", size, padding, | ||
356 | i2400m->tx_in % I2400M_TX_BUF_SIZE); | ||
357 | i2400m->tx_in += size; | ||
358 | return ptr; | ||
359 | } | ||
360 | |||
361 | |||
362 | /* | ||
363 | * Mark the tail of the FIFO buffer as 'to-skip' | ||
364 | * | ||
365 | * We should never hit the BUG_ON() because all the sizes we push to | ||
366 | * the FIFO are padded to be a multiple of 16 -- the size of *msg | ||
367 | * (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the | ||
368 | * header). | ||
369 | * | ||
370 | * Note: | ||
371 | * | ||
372 | * Assumes i2400m->tx_lock is taken, and we use that as a barrier | ||
373 | */ | ||
374 | static | ||
375 | void i2400m_tx_skip_tail(struct i2400m *i2400m) | ||
376 | { | ||
377 | struct device *dev = i2400m_dev(i2400m); | ||
378 | size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; | ||
379 | size_t tail_room = I2400M_TX_BUF_SIZE - tx_in; | ||
380 | struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in; | ||
381 | BUG_ON(tail_room < sizeof(*msg)); | ||
382 | msg->size = tail_room | I2400M_TX_SKIP; | ||
383 | d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n", | ||
384 | tail_room, tx_in); | ||
385 | i2400m->tx_in += tail_room; | ||
386 | } | ||
387 | |||
388 | |||
389 | /* | ||
390 | * Check if a skb will fit in the TX queue's current active TX | ||
391 | * message (if there are still descriptors left unused). | ||
392 | * | ||
393 | * Returns: | ||
394 | * 0 if the message won't fit, 1 if it will. | ||
395 | * | ||
396 | * Note: | ||
397 | * | ||
398 | * Assumes a TX message is active (i2400m->tx_msg). | ||
399 | * | ||
400 | * Assumes i2400m->tx_lock is taken, and we use that as a barrier | ||
401 | */ | ||
402 | static | ||
403 | unsigned i2400m_tx_fits(struct i2400m *i2400m) | ||
404 | { | ||
405 | struct i2400m_msg_hdr *msg_hdr = i2400m->tx_msg; | ||
406 | return le16_to_cpu(msg_hdr->num_pls) < I2400M_TX_PLD_MAX; | ||
407 | |||
408 | } | ||
409 | |||
410 | |||
411 | /* | ||
412 | * Start a new TX message header in the queue. | ||
413 | * | ||
414 | * Reserve memory from the base FIFO engine and then just initialize | ||
415 | * the message header. | ||
416 | * | ||
417 | * We allocate the biggest TX message header we might need (one that'd | ||
418 | * fit I2400M_TX_PLD_MAX payloads) -- when it is closed it will be | ||
419 | * 'ironed it out' and the unneeded parts removed. | ||
420 | * | ||
421 | * NOTE: | ||
422 | * | ||
423 | * Assumes that the previous message is CLOSED (eg: either | ||
424 | * there was none or 'i2400m_tx_close()' was called on it). | ||
425 | * | ||
426 | * Assumes i2400m->tx_lock is taken, and we use that as a barrier | ||
427 | */ | ||
428 | static | ||
429 | void i2400m_tx_new(struct i2400m *i2400m) | ||
430 | { | ||
431 | struct device *dev = i2400m_dev(i2400m); | ||
432 | struct i2400m_msg_hdr *tx_msg; | ||
433 | BUG_ON(i2400m->tx_msg != NULL); | ||
434 | try_head: | ||
435 | tx_msg = i2400m_tx_fifo_push(i2400m, I2400M_TX_PLD_SIZE, 0); | ||
436 | if (tx_msg == NULL) | ||
437 | goto out; | ||
438 | else if (tx_msg == TAIL_FULL) { | ||
439 | i2400m_tx_skip_tail(i2400m); | ||
440 | d_printf(2, dev, "new TX message: tail full, trying head\n"); | ||
441 | goto try_head; | ||
442 | } | ||
443 | memset(tx_msg, 0, I2400M_TX_PLD_SIZE); | ||
444 | tx_msg->size = I2400M_TX_PLD_SIZE; | ||
445 | out: | ||
446 | i2400m->tx_msg = tx_msg; | ||
447 | d_printf(2, dev, "new TX message: %p @%zu\n", | ||
448 | tx_msg, (void *) tx_msg - i2400m->tx_buf); | ||
449 | } | ||
450 | |||
451 | |||
452 | /* | ||
453 | * Finalize the current TX message header | ||
454 | * | ||
455 | * Sets the message header to be at the proper location depending on | ||
456 | * how many descriptors we have (check documentation at the file's | ||
457 | * header for more info on that). | ||
458 | * | ||
459 | * Appends padding bytes to make sure the whole TX message (counting | ||
460 | * from the 'relocated' message header) is aligned to | ||
461 | * tx_block_size. We assume the _append() code has left enough space | ||
462 | * in the FIFO for that. If there are no payloads, just pass, as it | ||
463 | * won't be transferred. | ||
464 | * | ||
465 | * The amount of padding bytes depends on how many payloads are in the | ||
466 | * TX message, as the "msg header and payload descriptors" will be | ||
467 | * shifted up in the buffer. | ||
468 | */ | ||
469 | static | ||
470 | void i2400m_tx_close(struct i2400m *i2400m) | ||
471 | { | ||
472 | struct device *dev = i2400m_dev(i2400m); | ||
473 | struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; | ||
474 | struct i2400m_msg_hdr *tx_msg_moved; | ||
475 | size_t aligned_size, padding, hdr_size; | ||
476 | void *pad_buf; | ||
477 | |||
478 | if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */ | ||
479 | goto out; | ||
480 | |||
481 | /* Relocate the message header | ||
482 | * | ||
483 | * Find the current header size, align it to 16 and if we need | ||
484 | * to move it so the tail is next to the payloads, move it and | ||
485 | * set the offset. | ||
486 | * | ||
487 | * If it moved, this header is good only for transmission; the | ||
488 | * original one (it is kept if we moved) is still used to | ||
489 | * figure out where the next TX message starts (and where the | ||
490 | * offset to the moved header is). | ||
491 | */ | ||
492 | hdr_size = sizeof(*tx_msg) | ||
493 | + le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]); | ||
494 | hdr_size = ALIGN(hdr_size, I2400M_PL_PAD); | ||
495 | tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size; | ||
496 | tx_msg_moved = (void *) tx_msg + tx_msg->offset; | ||
497 | memmove(tx_msg_moved, tx_msg, hdr_size); | ||
498 | tx_msg_moved->size -= tx_msg->offset; | ||
499 | /* | ||
500 | * Now figure out how much we have to add to the (moved!) | ||
501 | * message so the size is a multiple of i2400m->bus_tx_block_size. | ||
502 | */ | ||
503 | aligned_size = ALIGN(tx_msg_moved->size, i2400m->bus_tx_block_size); | ||
504 | padding = aligned_size - tx_msg_moved->size; | ||
505 | if (padding > 0) { | ||
506 | pad_buf = i2400m_tx_fifo_push(i2400m, padding, 0); | ||
507 | if (unlikely(WARN_ON(pad_buf == NULL | ||
508 | || pad_buf == TAIL_FULL))) { | ||
509 | /* This should not happen -- append should verify | ||
510 | * there is always space left at least to append | ||
511 | * tx_block_size */ | ||
512 | dev_err(dev, | ||
513 | "SW BUG! Possible data leakage from memory the " | ||
514 | "device should not read for padding - " | ||
515 | "size %lu aligned_size %zu tx_buf %p in " | ||
516 | "%zu out %zu\n", | ||
517 | (unsigned long) tx_msg_moved->size, | ||
518 | aligned_size, i2400m->tx_buf, i2400m->tx_in, | ||
519 | i2400m->tx_out); | ||
520 | } else | ||
521 | memset(pad_buf, 0xad, padding); | ||
522 | } | ||
523 | tx_msg_moved->padding = cpu_to_le16(padding); | ||
524 | tx_msg_moved->size += padding; | ||
525 | if (tx_msg != tx_msg_moved) | ||
526 | tx_msg->size += padding; | ||
527 | out: | ||
528 | i2400m->tx_msg = NULL; | ||
529 | } | ||
530 | |||
531 | |||
532 | /** | ||
533 | * i2400m_tx - send the data in a buffer to the device | ||
534 | * | ||
535 | * @buf: pointer to the buffer to transmit | ||
536 | * | ||
537 | * @buf_len: buffer size | ||
538 | * | ||
539 | * @pl_type: type of the payload we are sending. | ||
540 | * | ||
541 | * Returns: | ||
542 | * 0 if ok, < 0 errno code on error (-ENOSPC, if there is no more | ||
543 | * room for the message in the queue). | ||
544 | * | ||
545 | * Appends the buffer to the TX FIFO and notifies the bus-specific | ||
546 | * part of the driver that there is new data ready to transmit. | ||
547 | * Once this function returns, the buffer has been copied, so it can | ||
548 | * be reused. | ||
549 | * | ||
550 | * The steps followed to append are explained in detail in the file | ||
551 | * header. | ||
552 | * | ||
553 | * Whenever we write to a message, we increase msg->size, so it | ||
554 | * reflects exactly how big the message is. This is needed so that if | ||
555 | * we concatenate two messages before they can be sent, the code that | ||
556 | * sends the messages can find the boundaries (and it will replace the | ||
557 | * size with the real barker before sending). | ||
558 | * | ||
559 | * Note: | ||
560 | * | ||
561 | * Cold and warm reset payloads need to be sent as a single | ||
562 | * payload, so we handle that. | ||
563 | */ | ||
564 | int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, | ||
565 | enum i2400m_pt pl_type) | ||
566 | { | ||
567 | int result = -ENOSPC; | ||
568 | struct device *dev = i2400m_dev(i2400m); | ||
569 | unsigned long flags; | ||
570 | size_t padded_len; | ||
571 | void *ptr; | ||
572 | unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM | ||
573 | || pl_type == I2400M_PT_RESET_COLD; | ||
574 | |||
575 | d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", | ||
576 | i2400m, buf, buf_len, pl_type); | ||
577 | padded_len = ALIGN(buf_len, I2400M_PL_PAD); | ||
578 | d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); | ||
579 | /* If there is no current TX message, create one; if the | ||
580 | * current one is out of payload slots or we have a singleton, | ||
581 | * close it and start a new one */ | ||
582 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
583 | try_new: | ||
584 | if (unlikely(i2400m->tx_msg == NULL)) | ||
585 | i2400m_tx_new(i2400m); | ||
586 | else if (unlikely(!i2400m_tx_fits(i2400m) | ||
587 | || (is_singleton && i2400m->tx_msg->num_pls != 0))) { | ||
588 | d_printf(2, dev, "closing TX message (fits %u singleton " | ||
589 | "%u num_pls %u)\n", i2400m_tx_fits(i2400m), | ||
590 | is_singleton, i2400m->tx_msg->num_pls); | ||
591 | i2400m_tx_close(i2400m); | ||
592 | i2400m_tx_new(i2400m); | ||
593 | } | ||
594 | if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) { | ||
595 | d_printf(2, dev, "TX: message too big, going new\n"); | ||
596 | i2400m_tx_close(i2400m); | ||
597 | i2400m_tx_new(i2400m); | ||
598 | } | ||
599 | if (i2400m->tx_msg == NULL) | ||
600 | goto error_tx_new; | ||
601 | /* So we have a current message header; now append space for | ||
602 | * the message -- if there is not enough, try the head */ | ||
603 | ptr = i2400m_tx_fifo_push(i2400m, padded_len, | ||
604 | i2400m->bus_tx_block_size); | ||
605 | if (ptr == TAIL_FULL) { /* Tail is full, try head */ | ||
606 | d_printf(2, dev, "pl append: tail full\n"); | ||
607 | i2400m_tx_close(i2400m); | ||
608 | i2400m_tx_skip_tail(i2400m); | ||
609 | goto try_new; | ||
610 | } else if (ptr == NULL) { /* All full */ | ||
611 | result = -ENOSPC; | ||
612 | d_printf(2, dev, "pl append: all full\n"); | ||
613 | } else { /* Got space, copy it, set padding */ | ||
614 | struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; | ||
615 | unsigned num_pls = le16_to_cpu(tx_msg->num_pls); | ||
616 | memcpy(ptr, buf, buf_len); | ||
617 | memset(ptr + buf_len, 0xad, padded_len - buf_len); | ||
618 | i2400m_pld_set(&tx_msg->pld[num_pls], buf_len, pl_type); | ||
619 | d_printf(3, dev, "pld 0x%08x (type 0x%1x len 0x%04zx\n", | ||
620 | le32_to_cpu(tx_msg->pld[num_pls].val), | ||
621 | pl_type, buf_len); | ||
622 | tx_msg->num_pls = le16_to_cpu(num_pls+1); | ||
623 | tx_msg->size += padded_len; | ||
624 | d_printf(2, dev, "TX: appended %zu b (up to %u b) pl #%u \n", | ||
625 | padded_len, tx_msg->size, num_pls+1); | ||
626 | d_printf(2, dev, | ||
627 | "TX: appended hdr @%zu %zu b pl #%u @%zu %zu/%zu b\n", | ||
628 | (void *)tx_msg - i2400m->tx_buf, (size_t)tx_msg->size, | ||
629 | num_pls+1, ptr - i2400m->tx_buf, buf_len, padded_len); | ||
630 | result = 0; | ||
631 | if (is_singleton) | ||
632 | i2400m_tx_close(i2400m); | ||
633 | } | ||
634 | error_tx_new: | ||
635 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
636 | i2400m->bus_tx_kick(i2400m); /* always kick, might free up space */ | ||
637 | d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n", | ||
638 | i2400m, buf, buf_len, pl_type, result); | ||
639 | return result; | ||
640 | } | ||
641 | EXPORT_SYMBOL_GPL(i2400m_tx); | ||
642 | |||
643 | |||
644 | /** | ||
645 | * i2400m_tx_msg_get - Get the first TX message in the FIFO to start sending it | ||
646 | * | ||
647 | * @i2400m: device descriptors | ||
648 | * @bus_size: where to place the size of the TX message | ||
649 | * | ||
650 | * Called by the bus-specific driver to get the first TX message at | ||
651 | * the FIF that is ready for transmission. | ||
652 | * | ||
653 | * It sets the state in @i2400m to indicate the bus-specific driver is | ||
654 | * transfering that message (i2400m->tx_msg_size). | ||
655 | * | ||
656 | * Once the transfer is completed, call i2400m_tx_msg_sent(). | ||
657 | * | ||
658 | * Notes: | ||
659 | * | ||
660 | * The size of the TX message to be transmitted might be smaller than | ||
661 | * that of the TX message in the FIFO (in case the header was | ||
662 | * shorter). Hence, we copy it in @bus_size, for the bus layer to | ||
663 | * use. We keep the message's size in i2400m->tx_msg_size so that | ||
664 | * when the bus later is done transferring we know how much to | ||
665 | * advance the fifo. | ||
666 | * | ||
667 | * We collect statistics here as all the data is available and we | ||
668 | * assume it is going to work [see i2400m_tx_msg_sent()]. | ||
669 | */ | ||
670 | struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *i2400m, | ||
671 | size_t *bus_size) | ||
672 | { | ||
673 | struct device *dev = i2400m_dev(i2400m); | ||
674 | struct i2400m_msg_hdr *tx_msg, *tx_msg_moved; | ||
675 | unsigned long flags, pls; | ||
676 | |||
677 | d_fnstart(3, dev, "(i2400m %p bus_size %p)\n", i2400m, bus_size); | ||
678 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
679 | skip: | ||
680 | tx_msg_moved = NULL; | ||
681 | if (i2400m->tx_in == i2400m->tx_out) { /* Empty FIFO? */ | ||
682 | i2400m->tx_in = 0; | ||
683 | i2400m->tx_out = 0; | ||
684 | d_printf(2, dev, "TX: FIFO empty: resetting\n"); | ||
685 | goto out_unlock; | ||
686 | } | ||
687 | tx_msg = i2400m->tx_buf + i2400m->tx_out % I2400M_TX_BUF_SIZE; | ||
688 | if (tx_msg->size & I2400M_TX_SKIP) { /* skip? */ | ||
689 | d_printf(2, dev, "TX: skip: msg @%zu (%zu b)\n", | ||
690 | i2400m->tx_out % I2400M_TX_BUF_SIZE, | ||
691 | (size_t) tx_msg->size & ~I2400M_TX_SKIP); | ||
692 | i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP; | ||
693 | goto skip; | ||
694 | } | ||
695 | |||
696 | if (tx_msg->num_pls == 0) { /* No payloads? */ | ||
697 | if (tx_msg == i2400m->tx_msg) { /* open, we are done */ | ||
698 | d_printf(2, dev, | ||
699 | "TX: FIFO empty: open msg w/o payloads @%zu\n", | ||
700 | (void *) tx_msg - i2400m->tx_buf); | ||
701 | tx_msg = NULL; | ||
702 | goto out_unlock; | ||
703 | } else { /* closed, skip it */ | ||
704 | d_printf(2, dev, | ||
705 | "TX: skip msg w/o payloads @%zu (%zu b)\n", | ||
706 | (void *) tx_msg - i2400m->tx_buf, | ||
707 | (size_t) tx_msg->size); | ||
708 | i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP; | ||
709 | goto skip; | ||
710 | } | ||
711 | } | ||
712 | if (tx_msg == i2400m->tx_msg) /* open msg? */ | ||
713 | i2400m_tx_close(i2400m); | ||
714 | |||
715 | /* Now we have a valid TX message (with payloads) to TX */ | ||
716 | tx_msg_moved = (void *) tx_msg + tx_msg->offset; | ||
717 | i2400m->tx_msg_size = tx_msg->size; | ||
718 | *bus_size = tx_msg_moved->size; | ||
719 | d_printf(2, dev, "TX: pid %d msg hdr at @%zu offset +@%zu " | ||
720 | "size %zu bus_size %zu\n", | ||
721 | current->pid, (void *) tx_msg - i2400m->tx_buf, | ||
722 | (size_t) tx_msg->offset, (size_t) tx_msg->size, | ||
723 | (size_t) tx_msg_moved->size); | ||
724 | tx_msg_moved->barker = le32_to_cpu(I2400M_H2D_PREVIEW_BARKER); | ||
725 | tx_msg_moved->sequence = le32_to_cpu(i2400m->tx_sequence++); | ||
726 | |||
727 | pls = le32_to_cpu(tx_msg_moved->num_pls); | ||
728 | i2400m->tx_pl_num += pls; /* Update stats */ | ||
729 | if (pls > i2400m->tx_pl_max) | ||
730 | i2400m->tx_pl_max = pls; | ||
731 | if (pls < i2400m->tx_pl_min) | ||
732 | i2400m->tx_pl_min = pls; | ||
733 | i2400m->tx_num++; | ||
734 | i2400m->tx_size_acc += *bus_size; | ||
735 | if (*bus_size < i2400m->tx_size_min) | ||
736 | i2400m->tx_size_min = *bus_size; | ||
737 | if (*bus_size > i2400m->tx_size_max) | ||
738 | i2400m->tx_size_max = *bus_size; | ||
739 | out_unlock: | ||
740 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
741 | d_fnstart(3, dev, "(i2400m %p bus_size %p [%zu]) = %p\n", | ||
742 | i2400m, bus_size, *bus_size, tx_msg_moved); | ||
743 | return tx_msg_moved; | ||
744 | } | ||
745 | EXPORT_SYMBOL_GPL(i2400m_tx_msg_get); | ||
746 | |||
747 | |||
748 | /** | ||
749 | * i2400m_tx_msg_sent - indicate the transmission of a TX message | ||
750 | * | ||
751 | * @i2400m: device descriptor | ||
752 | * | ||
753 | * Called by the bus-specific driver when a message has been sent; | ||
754 | * this pops it from the FIFO; and as there is space, start the queue | ||
755 | * in case it was stopped. | ||
756 | * | ||
757 | * Should be called even if the message send failed and we are | ||
758 | * dropping this TX message. | ||
759 | */ | ||
760 | void i2400m_tx_msg_sent(struct i2400m *i2400m) | ||
761 | { | ||
762 | unsigned n; | ||
763 | unsigned long flags; | ||
764 | struct device *dev = i2400m_dev(i2400m); | ||
765 | |||
766 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
767 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
768 | i2400m->tx_out += i2400m->tx_msg_size; | ||
769 | d_printf(2, dev, "TX: sent %zu b\n", (size_t) i2400m->tx_msg_size); | ||
770 | i2400m->tx_msg_size = 0; | ||
771 | BUG_ON(i2400m->tx_out > i2400m->tx_in); | ||
772 | /* level them FIFO markers off */ | ||
773 | n = i2400m->tx_out / I2400M_TX_BUF_SIZE; | ||
774 | i2400m->tx_out %= I2400M_TX_BUF_SIZE; | ||
775 | i2400m->tx_in -= n * I2400M_TX_BUF_SIZE; | ||
776 | netif_start_queue(i2400m->wimax_dev.net_dev); | ||
777 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
778 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
779 | } | ||
780 | EXPORT_SYMBOL_GPL(i2400m_tx_msg_sent); | ||
781 | |||
782 | |||
783 | /** | ||
784 | * i2400m_tx_setup - Initialize the TX queue and infrastructure | ||
785 | * | ||
786 | * Make sure we reset the TX sequence to zero, as when this function | ||
787 | * is called, the firmware has been just restarted. | ||
788 | */ | ||
789 | int i2400m_tx_setup(struct i2400m *i2400m) | ||
790 | { | ||
791 | int result; | ||
792 | |||
793 | /* Do this here only once -- can't do on | ||
794 | * i2400m_hard_start_xmit() as we'll cause race conditions if | ||
795 | * the WS was scheduled on another CPU */ | ||
796 | INIT_WORK(&i2400m->wake_tx_ws, i2400m_wake_tx_work); | ||
797 | |||
798 | i2400m->tx_sequence = 0; | ||
799 | i2400m->tx_buf = kmalloc(I2400M_TX_BUF_SIZE, GFP_KERNEL); | ||
800 | if (i2400m->tx_buf == NULL) | ||
801 | result = -ENOMEM; | ||
802 | else | ||
803 | result = 0; | ||
804 | /* Huh? the bus layer has to define this... */ | ||
805 | BUG_ON(i2400m->bus_tx_block_size == 0); | ||
806 | return result; | ||
807 | |||
808 | } | ||
809 | |||
810 | |||
811 | /** | ||
812 | * i2400m_tx_release - Tear down the TX queue and infrastructure | ||
813 | */ | ||
814 | void i2400m_tx_release(struct i2400m *i2400m) | ||
815 | { | ||
816 | kfree(i2400m->tx_buf); | ||
817 | } | ||
diff --git a/drivers/net/wimax/i2400m/usb-debug-levels.h b/drivers/net/wimax/i2400m/usb-debug-levels.h new file mode 100644 index 000000000000..e4358bd880be --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-debug-levels.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Debug levels control file for the i2400m-usb module | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License version | ||
11 | * 2 as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | */ | ||
23 | #ifndef __debug_levels__h__ | ||
24 | #define __debug_levels__h__ | ||
25 | |||
26 | /* Maximum compile and run time debug level for all submodules */ | ||
27 | #define D_MODULENAME i2400m_usb | ||
28 | #define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL | ||
29 | |||
30 | #include <linux/wimax/debug.h> | ||
31 | |||
32 | /* List of all the enabled modules */ | ||
33 | enum d_module { | ||
34 | D_SUBMODULE_DECLARE(usb), | ||
35 | D_SUBMODULE_DECLARE(fw), | ||
36 | D_SUBMODULE_DECLARE(notif), | ||
37 | D_SUBMODULE_DECLARE(rx), | ||
38 | D_SUBMODULE_DECLARE(tx), | ||
39 | }; | ||
40 | |||
41 | |||
42 | #endif /* #ifndef __debug_levels__h__ */ | ||
diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c new file mode 100644 index 000000000000..5ad287c228b8 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-fw.c | |||
@@ -0,0 +1,340 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Firmware uploader's USB specifics | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
38 | * - Initial implementation | ||
39 | * | ||
40 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
41 | * - bus generic/specific split | ||
42 | * | ||
43 | * THE PROCEDURE | ||
44 | * | ||
45 | * See fw.c for the generic description of this procedure. | ||
46 | * | ||
47 | * This file implements only the USB specifics. It boils down to how | ||
48 | * to send a command and waiting for an acknowledgement from the | ||
49 | * device. | ||
50 | * | ||
51 | * This code (and process) is single threaded. It assumes it is the | ||
52 | * only thread poking around (guaranteed by fw.c). | ||
53 | * | ||
54 | * COMMAND EXECUTION | ||
55 | * | ||
56 | * A write URB is posted with the buffer to the bulk output endpoint. | ||
57 | * | ||
58 | * ACK RECEPTION | ||
59 | * | ||
60 | * We just post a URB to the notification endpoint and wait for | ||
61 | * data. We repeat until we get all the data we expect (as indicated | ||
62 | * by the call from the bus generic code). | ||
63 | * | ||
64 | * The data is not read from the bulk in endpoint for boot mode. | ||
65 | * | ||
66 | * ROADMAP | ||
67 | * | ||
68 | * i2400mu_bus_bm_cmd_send | ||
69 | * i2400m_bm_cmd_prepare... | ||
70 | * i2400mu_tx_bulk_out | ||
71 | * | ||
72 | * i2400mu_bus_bm_wait_for_ack | ||
73 | * i2400m_notif_submit | ||
74 | */ | ||
75 | #include <linux/usb.h> | ||
76 | #include "i2400m-usb.h" | ||
77 | |||
78 | |||
79 | #define D_SUBMODULE fw | ||
80 | #include "usb-debug-levels.h" | ||
81 | |||
82 | |||
83 | /* | ||
84 | * Synchronous write to the device | ||
85 | * | ||
86 | * Takes care of updating EDC counts and thus, handle device errors. | ||
87 | */ | ||
88 | static | ||
89 | ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size) | ||
90 | { | ||
91 | int result; | ||
92 | struct device *dev = &i2400mu->usb_iface->dev; | ||
93 | int len; | ||
94 | struct usb_endpoint_descriptor *epd; | ||
95 | int pipe, do_autopm = 1; | ||
96 | |||
97 | result = usb_autopm_get_interface(i2400mu->usb_iface); | ||
98 | if (result < 0) { | ||
99 | dev_err(dev, "BM-CMD: can't get autopm: %d\n", result); | ||
100 | do_autopm = 0; | ||
101 | } | ||
102 | epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT); | ||
103 | pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); | ||
104 | retry: | ||
105 | result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, HZ); | ||
106 | switch (result) { | ||
107 | case 0: | ||
108 | if (len != buf_size) { | ||
109 | dev_err(dev, "BM-CMD: short write (%u B vs %zu " | ||
110 | "expected)\n", len, buf_size); | ||
111 | result = -EIO; | ||
112 | break; | ||
113 | } | ||
114 | result = len; | ||
115 | break; | ||
116 | case -EINVAL: /* while removing driver */ | ||
117 | case -ENODEV: /* dev disconnect ... */ | ||
118 | case -ENOENT: /* just ignore it */ | ||
119 | case -ESHUTDOWN: /* and exit */ | ||
120 | case -ECONNRESET: | ||
121 | result = -ESHUTDOWN; | ||
122 | break; | ||
123 | case -ETIMEDOUT: /* bah... */ | ||
124 | break; | ||
125 | default: /* any other? */ | ||
126 | if (edc_inc(&i2400mu->urb_edc, | ||
127 | EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { | ||
128 | dev_err(dev, "BM-CMD: maximum errors in " | ||
129 | "URB exceeded; resetting device\n"); | ||
130 | usb_queue_reset_device(i2400mu->usb_iface); | ||
131 | result = -ENODEV; | ||
132 | break; | ||
133 | } | ||
134 | dev_err(dev, "BM-CMD: URB error %d, retrying\n", | ||
135 | result); | ||
136 | goto retry; | ||
137 | } | ||
138 | result = len; | ||
139 | if (do_autopm) | ||
140 | usb_autopm_put_interface(i2400mu->usb_iface); | ||
141 | return result; | ||
142 | } | ||
143 | |||
144 | |||
145 | /* | ||
146 | * Send a boot-mode command over the bulk-out pipe | ||
147 | * | ||
148 | * Command can be a raw command, which requires no preparation (and | ||
149 | * which might not even be following the command format). Checks that | ||
150 | * the right amount of data was transfered. | ||
151 | * | ||
152 | * To satisfy USB requirements (no onstack, vmalloc or in data segment | ||
153 | * buffers), we copy the command to i2400m->bm_cmd_buf and send it from | ||
154 | * there. | ||
155 | * | ||
156 | * @flags: pass thru from i2400m_bm_cmd() | ||
157 | * @return: cmd_size if ok, < 0 errno code on error. | ||
158 | */ | ||
159 | ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m, | ||
160 | const struct i2400m_bootrom_header *_cmd, | ||
161 | size_t cmd_size, int flags) | ||
162 | { | ||
163 | ssize_t result; | ||
164 | struct device *dev = i2400m_dev(i2400m); | ||
165 | struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); | ||
166 | int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd); | ||
167 | struct i2400m_bootrom_header *cmd; | ||
168 | size_t cmd_size_a = ALIGN(cmd_size, 16); /* USB restriction */ | ||
169 | |||
170 | d_fnstart(8, dev, "(i2400m %p cmd %p size %zu)\n", | ||
171 | i2400m, _cmd, cmd_size); | ||
172 | result = -E2BIG; | ||
173 | if (cmd_size > I2400M_BM_CMD_BUF_SIZE) | ||
174 | goto error_too_big; | ||
175 | memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); | ||
176 | cmd = i2400m->bm_cmd_buf; | ||
177 | if (cmd_size_a > cmd_size) /* Zero pad space */ | ||
178 | memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); | ||
179 | if ((flags & I2400M_BM_CMD_RAW) == 0) { | ||
180 | if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0)) | ||
181 | dev_warn(dev, "SW BUG: response_required == 0\n"); | ||
182 | i2400m_bm_cmd_prepare(cmd); | ||
183 | } | ||
184 | result = i2400mu_tx_bulk_out(i2400mu, i2400m->bm_cmd_buf, cmd_size); | ||
185 | if (result < 0) { | ||
186 | dev_err(dev, "boot-mode cmd %d: cannot send: %zd\n", | ||
187 | opcode, result); | ||
188 | goto error_cmd_send; | ||
189 | } | ||
190 | if (result != cmd_size) { /* all was transferred? */ | ||
191 | dev_err(dev, "boot-mode cmd %d: incomplete transfer " | ||
192 | "(%zu vs %zu submitted)\n", opcode, result, cmd_size); | ||
193 | result = -EIO; | ||
194 | goto error_cmd_size; | ||
195 | } | ||
196 | error_cmd_size: | ||
197 | error_cmd_send: | ||
198 | error_too_big: | ||
199 | d_fnend(8, dev, "(i2400m %p cmd %p size %zu) = %zd\n", | ||
200 | i2400m, _cmd, cmd_size, result); | ||
201 | return result; | ||
202 | } | ||
203 | |||
204 | |||
205 | static | ||
206 | void __i2400mu_bm_notif_cb(struct urb *urb) | ||
207 | { | ||
208 | complete(urb->context); | ||
209 | } | ||
210 | |||
211 | |||
212 | /* | ||
213 | * submit a read to the notification endpoint | ||
214 | * | ||
215 | * @i2400m: device descriptor | ||
216 | * @urb: urb to use | ||
217 | * @completion: completion varible to complete when done | ||
218 | * | ||
219 | * Data is always read to i2400m->bm_ack_buf | ||
220 | */ | ||
221 | static | ||
222 | int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb, | ||
223 | struct completion *completion) | ||
224 | { | ||
225 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
226 | struct usb_endpoint_descriptor *epd; | ||
227 | int pipe; | ||
228 | |||
229 | epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION); | ||
230 | pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress); | ||
231 | usb_fill_int_urb(urb, i2400mu->usb_dev, pipe, | ||
232 | i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE, | ||
233 | __i2400mu_bm_notif_cb, completion, | ||
234 | epd->bInterval); | ||
235 | return usb_submit_urb(urb, GFP_KERNEL); | ||
236 | } | ||
237 | |||
238 | |||
239 | /* | ||
240 | * Read an ack from the notification endpoint | ||
241 | * | ||
242 | * @i2400m: | ||
243 | * @_ack: pointer to where to store the read data | ||
244 | * @ack_size: how many bytes we should read | ||
245 | * | ||
246 | * Returns: < 0 errno code on error; otherwise, amount of received bytes. | ||
247 | * | ||
248 | * Submits a notification read, appends the read data to the given ack | ||
249 | * buffer and then repeats (until @ack_size bytes have been | ||
250 | * received). | ||
251 | */ | ||
252 | ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *i2400m, | ||
253 | struct i2400m_bootrom_header *_ack, | ||
254 | size_t ack_size) | ||
255 | { | ||
256 | ssize_t result = -ENOMEM; | ||
257 | struct device *dev = i2400m_dev(i2400m); | ||
258 | struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); | ||
259 | struct urb notif_urb; | ||
260 | void *ack = _ack; | ||
261 | size_t offset, len; | ||
262 | long val; | ||
263 | int do_autopm = 1; | ||
264 | DECLARE_COMPLETION_ONSTACK(notif_completion); | ||
265 | |||
266 | d_fnstart(8, dev, "(i2400m %p ack %p size %zu)\n", | ||
267 | i2400m, ack, ack_size); | ||
268 | BUG_ON(_ack == i2400m->bm_ack_buf); | ||
269 | result = usb_autopm_get_interface(i2400mu->usb_iface); | ||
270 | if (result < 0) { | ||
271 | dev_err(dev, "BM-ACK: can't get autopm: %d\n", (int) result); | ||
272 | do_autopm = 0; | ||
273 | } | ||
274 | usb_init_urb(¬if_urb); /* ready notifications */ | ||
275 | usb_get_urb(¬if_urb); | ||
276 | offset = 0; | ||
277 | while (offset < ack_size) { | ||
278 | init_completion(¬if_completion); | ||
279 | result = i2400mu_notif_submit(i2400mu, ¬if_urb, | ||
280 | ¬if_completion); | ||
281 | if (result < 0) | ||
282 | goto error_notif_urb_submit; | ||
283 | val = wait_for_completion_interruptible_timeout( | ||
284 | ¬if_completion, HZ); | ||
285 | if (val == 0) { | ||
286 | result = -ETIMEDOUT; | ||
287 | usb_kill_urb(¬if_urb); /* Timedout */ | ||
288 | goto error_notif_wait; | ||
289 | } | ||
290 | if (val == -ERESTARTSYS) { | ||
291 | result = -EINTR; /* Interrupted */ | ||
292 | usb_kill_urb(¬if_urb); | ||
293 | goto error_notif_wait; | ||
294 | } | ||
295 | result = notif_urb.status; /* How was the ack? */ | ||
296 | switch (result) { | ||
297 | case 0: | ||
298 | break; | ||
299 | case -EINVAL: /* while removing driver */ | ||
300 | case -ENODEV: /* dev disconnect ... */ | ||
301 | case -ENOENT: /* just ignore it */ | ||
302 | case -ESHUTDOWN: /* and exit */ | ||
303 | case -ECONNRESET: | ||
304 | result = -ESHUTDOWN; | ||
305 | goto error_dev_gone; | ||
306 | default: /* any other? */ | ||
307 | usb_kill_urb(¬if_urb); /* Timedout */ | ||
308 | if (edc_inc(&i2400mu->urb_edc, | ||
309 | EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) | ||
310 | goto error_exceeded; | ||
311 | dev_err(dev, "BM-ACK: URB error %d, " | ||
312 | "retrying\n", notif_urb.status); | ||
313 | continue; /* retry */ | ||
314 | } | ||
315 | if (notif_urb.actual_length == 0) { | ||
316 | d_printf(6, dev, "ZLP received, retrying\n"); | ||
317 | continue; | ||
318 | } | ||
319 | /* Got data, append it to the buffer */ | ||
320 | len = min(ack_size - offset, (size_t) notif_urb.actual_length); | ||
321 | memcpy(ack + offset, i2400m->bm_ack_buf, len); | ||
322 | offset += len; | ||
323 | } | ||
324 | result = offset; | ||
325 | error_notif_urb_submit: | ||
326 | error_notif_wait: | ||
327 | error_dev_gone: | ||
328 | out: | ||
329 | if (do_autopm) | ||
330 | usb_autopm_put_interface(i2400mu->usb_iface); | ||
331 | d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %zd\n", | ||
332 | i2400m, ack, ack_size, result); | ||
333 | return result; | ||
334 | |||
335 | error_exceeded: | ||
336 | dev_err(dev, "bm: maximum errors in notification URB exceeded; " | ||
337 | "resetting device\n"); | ||
338 | usb_queue_reset_device(i2400mu->usb_iface); | ||
339 | goto out; | ||
340 | } | ||
diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c new file mode 100644 index 000000000000..9702c22b2497 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-notif.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m over USB | ||
3 | * Notification handling | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
38 | * - Initial implementation | ||
39 | * | ||
40 | * | ||
41 | * The notification endpoint is active when the device is not in boot | ||
42 | * mode; in here we just read and get notifications; based on those, | ||
43 | * we act to either reinitialize the device after a reboot or to | ||
44 | * submit a RX request. | ||
45 | * | ||
46 | * ROADMAP | ||
47 | * | ||
48 | * i2400mu_usb_notification_setup() | ||
49 | * | ||
50 | * i2400mu_usb_notification_release() | ||
51 | * | ||
52 | * i2400mu_usb_notification_cb() Called when a URB is ready | ||
53 | * i2400mu_notif_grok() | ||
54 | * i2400m_dev_reset_handle() | ||
55 | * i2400mu_rx_kick() | ||
56 | */ | ||
57 | #include <linux/usb.h> | ||
58 | #include "i2400m-usb.h" | ||
59 | |||
60 | |||
61 | #define D_SUBMODULE notif | ||
62 | #include "usb-debug-levels.h" | ||
63 | |||
64 | |||
65 | static const | ||
66 | __le32 i2400m_ZERO_BARKER[4] = { 0, 0, 0, 0 }; | ||
67 | |||
68 | |||
69 | /* | ||
70 | * Process a received notification | ||
71 | * | ||
72 | * In normal operation mode, we can only receive two types of payloads | ||
73 | * on the notification endpoint: | ||
74 | * | ||
75 | * - a reboot barker, we do a bootstrap (the device has reseted). | ||
76 | * | ||
77 | * - a block of zeroes: there is pending data in the IN endpoint | ||
78 | */ | ||
79 | static | ||
80 | int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf, | ||
81 | size_t buf_len) | ||
82 | { | ||
83 | int ret; | ||
84 | struct device *dev = &i2400mu->usb_iface->dev; | ||
85 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
86 | |||
87 | d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n", | ||
88 | i2400mu, buf, buf_len); | ||
89 | ret = -EIO; | ||
90 | if (buf_len < sizeof(i2400m_NBOOT_BARKER)) | ||
91 | /* Not a bug, just ignore */ | ||
92 | goto error_bad_size; | ||
93 | if (!memcmp(i2400m_NBOOT_BARKER, buf, sizeof(i2400m_NBOOT_BARKER)) | ||
94 | || !memcmp(i2400m_SBOOT_BARKER, buf, sizeof(i2400m_SBOOT_BARKER))) | ||
95 | ret = i2400m_dev_reset_handle(i2400m); | ||
96 | else if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) { | ||
97 | i2400mu_rx_kick(i2400mu); | ||
98 | ret = 0; | ||
99 | } else { /* Unknown or unexpected data in the notif message */ | ||
100 | char prefix[64]; | ||
101 | ret = -EIO; | ||
102 | dev_err(dev, "HW BUG? Unknown/unexpected data in notification " | ||
103 | "message (%zu bytes)\n", buf_len); | ||
104 | snprintf(prefix, sizeof(prefix), "%s %s: ", | ||
105 | dev_driver_string(dev) , dev->bus_id); | ||
106 | if (buf_len > 64) { | ||
107 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
108 | 8, 4, buf, 64, 0); | ||
109 | printk(KERN_ERR "%s... (only first 64 bytes " | ||
110 | "dumped)\n", prefix); | ||
111 | } else | ||
112 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
113 | 8, 4, buf, buf_len, 0); | ||
114 | } | ||
115 | error_bad_size: | ||
116 | d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n", | ||
117 | i2400mu, buf, buf_len, ret); | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | |||
122 | /* | ||
123 | * URB callback for the notification endpoint | ||
124 | * | ||
125 | * @urb: the urb received from the notification endpoint | ||
126 | * | ||
127 | * This function will just process the USB side of the transaction, | ||
128 | * checking everything is fine, pass the processing to | ||
129 | * i2400m_notification_grok() and resubmit the URB. | ||
130 | */ | ||
131 | static | ||
132 | void i2400mu_notification_cb(struct urb *urb) | ||
133 | { | ||
134 | int ret; | ||
135 | struct i2400mu *i2400mu = urb->context; | ||
136 | struct device *dev = &i2400mu->usb_iface->dev; | ||
137 | |||
138 | d_fnstart(4, dev, "(urb %p status %d actual_length %d)\n", | ||
139 | urb, urb->status, urb->actual_length); | ||
140 | ret = urb->status; | ||
141 | switch (ret) { | ||
142 | case 0: | ||
143 | ret = i2400mu_notification_grok(i2400mu, urb->transfer_buffer, | ||
144 | urb->actual_length); | ||
145 | if (ret == -EIO && edc_inc(&i2400mu->urb_edc, EDC_MAX_ERRORS, | ||
146 | EDC_ERROR_TIMEFRAME)) | ||
147 | goto error_exceeded; | ||
148 | if (ret == -ENOMEM) /* uff...power cycle? shutdown? */ | ||
149 | goto error_exceeded; | ||
150 | break; | ||
151 | case -EINVAL: /* while removing driver */ | ||
152 | case -ENODEV: /* dev disconnect ... */ | ||
153 | case -ENOENT: /* ditto */ | ||
154 | case -ESHUTDOWN: /* URB killed */ | ||
155 | case -ECONNRESET: /* disconnection */ | ||
156 | goto out; /* Notify around */ | ||
157 | default: /* Some error? */ | ||
158 | if (edc_inc(&i2400mu->urb_edc, | ||
159 | EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) | ||
160 | goto error_exceeded; | ||
161 | dev_err(dev, "notification: URB error %d, retrying\n", | ||
162 | urb->status); | ||
163 | } | ||
164 | usb_mark_last_busy(i2400mu->usb_dev); | ||
165 | ret = usb_submit_urb(i2400mu->notif_urb, GFP_ATOMIC); | ||
166 | switch (ret) { | ||
167 | case 0: | ||
168 | case -EINVAL: /* while removing driver */ | ||
169 | case -ENODEV: /* dev disconnect ... */ | ||
170 | case -ENOENT: /* ditto */ | ||
171 | case -ESHUTDOWN: /* URB killed */ | ||
172 | case -ECONNRESET: /* disconnection */ | ||
173 | break; /* just ignore */ | ||
174 | default: /* Some error? */ | ||
175 | dev_err(dev, "notification: cannot submit URB: %d\n", ret); | ||
176 | goto error_submit; | ||
177 | } | ||
178 | d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n", | ||
179 | urb, urb->status, urb->actual_length); | ||
180 | return; | ||
181 | |||
182 | error_exceeded: | ||
183 | dev_err(dev, "maximum errors in notification URB exceeded; " | ||
184 | "resetting device\n"); | ||
185 | error_submit: | ||
186 | usb_queue_reset_device(i2400mu->usb_iface); | ||
187 | out: | ||
188 | d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n", | ||
189 | urb, urb->status, urb->actual_length); | ||
190 | return; | ||
191 | } | ||
192 | |||
193 | |||
194 | /* | ||
195 | * setup the notification endpoint | ||
196 | * | ||
197 | * @i2400m: device descriptor | ||
198 | * | ||
199 | * This procedure prepares the notification urb and handler for receiving | ||
200 | * unsolicited barkers from the device. | ||
201 | */ | ||
202 | int i2400mu_notification_setup(struct i2400mu *i2400mu) | ||
203 | { | ||
204 | struct device *dev = &i2400mu->usb_iface->dev; | ||
205 | int usb_pipe, ret = 0; | ||
206 | struct usb_endpoint_descriptor *epd; | ||
207 | char *buf; | ||
208 | |||
209 | d_fnstart(4, dev, "(i2400m %p)\n", i2400mu); | ||
210 | buf = kmalloc(I2400MU_MAX_NOTIFICATION_LEN, GFP_KERNEL | GFP_DMA); | ||
211 | if (buf == NULL) { | ||
212 | dev_err(dev, "notification: buffer allocation failed\n"); | ||
213 | ret = -ENOMEM; | ||
214 | goto error_buf_alloc; | ||
215 | } | ||
216 | |||
217 | i2400mu->notif_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
218 | if (!i2400mu->notif_urb) { | ||
219 | ret = -ENOMEM; | ||
220 | dev_err(dev, "notification: cannot allocate URB\n"); | ||
221 | goto error_alloc_urb; | ||
222 | } | ||
223 | epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION); | ||
224 | usb_pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress); | ||
225 | usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe, | ||
226 | buf, I2400MU_MAX_NOTIFICATION_LEN, | ||
227 | i2400mu_notification_cb, i2400mu, epd->bInterval); | ||
228 | ret = usb_submit_urb(i2400mu->notif_urb, GFP_KERNEL); | ||
229 | if (ret != 0) { | ||
230 | dev_err(dev, "notification: cannot submit URB: %d\n", ret); | ||
231 | goto error_submit; | ||
232 | } | ||
233 | d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret); | ||
234 | return ret; | ||
235 | |||
236 | error_submit: | ||
237 | usb_free_urb(i2400mu->notif_urb); | ||
238 | error_alloc_urb: | ||
239 | kfree(buf); | ||
240 | error_buf_alloc: | ||
241 | d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret); | ||
242 | return ret; | ||
243 | } | ||
244 | |||
245 | |||
246 | /* | ||
247 | * Tear down of the notification mechanism | ||
248 | * | ||
249 | * @i2400m: device descriptor | ||
250 | * | ||
251 | * Kill the interrupt endpoint urb, free any allocated resources. | ||
252 | * | ||
253 | * We need to check if we have done it before as for example, | ||
254 | * _suspend() call this; if after a suspend() we get a _disconnect() | ||
255 | * (as the case is when hibernating), nothing bad happens. | ||
256 | */ | ||
257 | void i2400mu_notification_release(struct i2400mu *i2400mu) | ||
258 | { | ||
259 | struct device *dev = &i2400mu->usb_iface->dev; | ||
260 | |||
261 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); | ||
262 | if (i2400mu->notif_urb != NULL) { | ||
263 | usb_kill_urb(i2400mu->notif_urb); | ||
264 | kfree(i2400mu->notif_urb->transfer_buffer); | ||
265 | usb_free_urb(i2400mu->notif_urb); | ||
266 | i2400mu->notif_urb = NULL; | ||
267 | } | ||
268 | d_fnend(4, dev, "(i2400mu %p)\n", i2400mu); | ||
269 | } | ||
diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c new file mode 100644 index 000000000000..074cc1f89853 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-rx.c | |||
@@ -0,0 +1,417 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * USB RX handling | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * - Initial implementation | ||
38 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
39 | * - Use skb_clone(), break up processing in chunks | ||
40 | * - Split transport/device specific | ||
41 | * - Make buffer size dynamic to exert less memory pressure | ||
42 | * | ||
43 | * | ||
44 | * This handles the RX path on USB. | ||
45 | * | ||
46 | * When a notification is received that says 'there is RX data ready', | ||
47 | * we call i2400mu_rx_kick(); that wakes up the RX kthread, which | ||
48 | * reads a buffer from USB and passes it to i2400m_rx() in the generic | ||
49 | * handling code. The RX buffer has an specific format that is | ||
50 | * described in rx.c. | ||
51 | * | ||
52 | * We use a kernel thread in a loop because: | ||
53 | * | ||
54 | * - we want to be able to call the USB power management get/put | ||
55 | * functions (blocking) before each transaction. | ||
56 | * | ||
57 | * - We might get a lot of notifications and we don't want to submit | ||
58 | * a zillion reads; by serializing, we are throttling. | ||
59 | * | ||
60 | * - RX data processing can get heavy enough so that it is not | ||
61 | * appropiate for doing it in the USB callback; thus we run it in a | ||
62 | * process context. | ||
63 | * | ||
64 | * We provide a read buffer of an arbitrary size (short of a page); if | ||
65 | * the callback reports -EOVERFLOW, it means it was too small, so we | ||
66 | * just double the size and retry (being careful to append, as | ||
67 | * sometimes the device provided some data). Every now and then we | ||
68 | * check if the average packet size is smaller than the current packet | ||
69 | * size and if so, we halve it. At the end, the size of the | ||
70 | * preallocated buffer should be following the average received | ||
71 | * transaction size, adapting dynamically to it. | ||
72 | * | ||
73 | * ROADMAP | ||
74 | * | ||
75 | * i2400mu_rx_kick() Called from notif.c when we get a | ||
76 | * 'data ready' notification | ||
77 | * i2400mu_rxd() Kernel RX daemon | ||
78 | * i2400mu_rx() Receive USB data | ||
79 | * i2400m_rx() Send data to generic i2400m RX handling | ||
80 | * | ||
81 | * i2400mu_rx_setup() called from i2400mu_bus_dev_start() | ||
82 | * | ||
83 | * i2400mu_rx_release() called from i2400mu_bus_dev_stop() | ||
84 | */ | ||
85 | #include <linux/workqueue.h> | ||
86 | #include <linux/usb.h> | ||
87 | #include "i2400m-usb.h" | ||
88 | |||
89 | |||
90 | #define D_SUBMODULE rx | ||
91 | #include "usb-debug-levels.h" | ||
92 | |||
93 | /* | ||
94 | * Dynamic RX size | ||
95 | * | ||
96 | * We can't let the rx_size be a multiple of 512 bytes (the RX | ||
97 | * endpoint's max packet size). On some USB host controllers (we | ||
98 | * haven't been able to fully characterize which), if the device is | ||
99 | * about to send (for example) X bytes and we only post a buffer to | ||
100 | * receive n*512, it will fail to mark that as babble (so that | ||
101 | * i2400mu_rx() [case -EOVERFLOW] can resize the buffer and get the | ||
102 | * rest). | ||
103 | * | ||
104 | * So on growing or shrinking, if it is a multiple of the | ||
105 | * maxpacketsize, we remove some (instead of incresing some, so in a | ||
106 | * buddy allocator we try to waste less space). | ||
107 | * | ||
108 | * Note we also need a hook for this on i2400mu_rx() -- when we do the | ||
109 | * first read, we are sure we won't hit this spot because | ||
110 | * i240mm->rx_size has been set properly. However, if we have to | ||
111 | * double because of -EOVERFLOW, when we launch the read to get the | ||
112 | * rest of the data, we *have* to make sure that also is not a | ||
113 | * multiple of the max_pkt_size. | ||
114 | */ | ||
115 | |||
116 | static | ||
117 | size_t i2400mu_rx_size_grow(struct i2400mu *i2400mu) | ||
118 | { | ||
119 | struct device *dev = &i2400mu->usb_iface->dev; | ||
120 | size_t rx_size; | ||
121 | const size_t max_pkt_size = 512; | ||
122 | |||
123 | rx_size = 2 * i2400mu->rx_size; | ||
124 | if (rx_size % max_pkt_size == 0) { | ||
125 | rx_size -= 8; | ||
126 | d_printf(1, dev, | ||
127 | "RX: expected size grew to %zu [adjusted -8] " | ||
128 | "from %zu\n", | ||
129 | rx_size, i2400mu->rx_size); | ||
130 | } else | ||
131 | d_printf(1, dev, | ||
132 | "RX: expected size grew to %zu from %zu\n", | ||
133 | rx_size, i2400mu->rx_size); | ||
134 | return rx_size; | ||
135 | } | ||
136 | |||
137 | |||
138 | static | ||
139 | void i2400mu_rx_size_maybe_shrink(struct i2400mu *i2400mu) | ||
140 | { | ||
141 | const size_t max_pkt_size = 512; | ||
142 | struct device *dev = &i2400mu->usb_iface->dev; | ||
143 | |||
144 | if (unlikely(i2400mu->rx_size_cnt >= 100 | ||
145 | && i2400mu->rx_size_auto_shrink)) { | ||
146 | size_t avg_rx_size = | ||
147 | i2400mu->rx_size_acc / i2400mu->rx_size_cnt; | ||
148 | size_t new_rx_size = i2400mu->rx_size / 2; | ||
149 | if (avg_rx_size < new_rx_size) { | ||
150 | if (new_rx_size % max_pkt_size == 0) { | ||
151 | new_rx_size -= 8; | ||
152 | d_printf(1, dev, | ||
153 | "RX: expected size shrank to %zu " | ||
154 | "[adjusted -8] from %zu\n", | ||
155 | new_rx_size, i2400mu->rx_size); | ||
156 | } else | ||
157 | d_printf(1, dev, | ||
158 | "RX: expected size shrank to %zu " | ||
159 | "from %zu\n", | ||
160 | new_rx_size, i2400mu->rx_size); | ||
161 | i2400mu->rx_size = new_rx_size; | ||
162 | i2400mu->rx_size_cnt = 0; | ||
163 | i2400mu->rx_size_acc = i2400mu->rx_size; | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Receive a message with payloads from the USB bus into an skb | ||
170 | * | ||
171 | * @i2400mu: USB device descriptor | ||
172 | * @rx_skb: skb where to place the received message | ||
173 | * | ||
174 | * Deals with all the USB-specifics of receiving, dynamically | ||
175 | * increasing the buffer size if so needed. Returns the payload in the | ||
176 | * skb, ready to process. On a zero-length packet, we retry. | ||
177 | * | ||
178 | * On soft USB errors, we retry (until they become too frequent and | ||
179 | * then are promoted to hard); on hard USB errors, we reset the | ||
180 | * device. On other errors (skb realloacation, we just drop it and | ||
181 | * hope for the next invocation to solve it). | ||
182 | * | ||
183 | * Returns: pointer to the skb if ok, ERR_PTR on error. | ||
184 | * NOTE: this function might realloc the skb (if it is too small), | ||
185 | * so always update with the one returned. | ||
186 | * ERR_PTR() is < 0 on error. | ||
187 | */ | ||
188 | static | ||
189 | struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb) | ||
190 | { | ||
191 | int result = 0; | ||
192 | struct device *dev = &i2400mu->usb_iface->dev; | ||
193 | int usb_pipe, read_size, rx_size, do_autopm; | ||
194 | struct usb_endpoint_descriptor *epd; | ||
195 | const size_t max_pkt_size = 512; | ||
196 | |||
197 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); | ||
198 | do_autopm = atomic_read(&i2400mu->do_autopm); | ||
199 | result = do_autopm ? | ||
200 | usb_autopm_get_interface(i2400mu->usb_iface) : 0; | ||
201 | if (result < 0) { | ||
202 | dev_err(dev, "RX: can't get autopm: %d\n", result); | ||
203 | do_autopm = 0; | ||
204 | } | ||
205 | epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_IN); | ||
206 | usb_pipe = usb_rcvbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); | ||
207 | retry: | ||
208 | rx_size = skb_end_pointer(rx_skb) - rx_skb->data - rx_skb->len; | ||
209 | if (unlikely(rx_size % max_pkt_size == 0)) { | ||
210 | rx_size -= 8; | ||
211 | d_printf(1, dev, "RX: rx_size adapted to %d [-8]\n", rx_size); | ||
212 | } | ||
213 | result = usb_bulk_msg( | ||
214 | i2400mu->usb_dev, usb_pipe, rx_skb->data + rx_skb->len, | ||
215 | rx_size, &read_size, HZ); | ||
216 | usb_mark_last_busy(i2400mu->usb_dev); | ||
217 | switch (result) { | ||
218 | case 0: | ||
219 | if (read_size == 0) | ||
220 | goto retry; /* ZLP, just resubmit */ | ||
221 | skb_put(rx_skb, read_size); | ||
222 | break; | ||
223 | case -EINVAL: /* while removing driver */ | ||
224 | case -ENODEV: /* dev disconnect ... */ | ||
225 | case -ENOENT: /* just ignore it */ | ||
226 | case -ESHUTDOWN: | ||
227 | case -ECONNRESET: | ||
228 | break; | ||
229 | case -EOVERFLOW: { /* too small, reallocate */ | ||
230 | struct sk_buff *new_skb; | ||
231 | rx_size = i2400mu_rx_size_grow(i2400mu); | ||
232 | if (rx_size <= (1 << 16)) /* cap it */ | ||
233 | i2400mu->rx_size = rx_size; | ||
234 | else if (printk_ratelimit()) { | ||
235 | dev_err(dev, "BUG? rx_size up to %d\n", rx_size); | ||
236 | result = -EINVAL; | ||
237 | goto out; | ||
238 | } | ||
239 | skb_put(rx_skb, read_size); | ||
240 | new_skb = skb_copy_expand(rx_skb, 0, rx_size - rx_skb->len, | ||
241 | GFP_KERNEL); | ||
242 | if (new_skb == NULL) { | ||
243 | if (printk_ratelimit()) | ||
244 | dev_err(dev, "RX: Can't reallocate skb to %d; " | ||
245 | "RX dropped\n", rx_size); | ||
246 | kfree(rx_skb); | ||
247 | result = 0; | ||
248 | goto out; /* drop it...*/ | ||
249 | } | ||
250 | kfree_skb(rx_skb); | ||
251 | rx_skb = new_skb; | ||
252 | i2400mu->rx_size_cnt = 0; | ||
253 | i2400mu->rx_size_acc = i2400mu->rx_size; | ||
254 | d_printf(1, dev, "RX: size changed to %d, received %d, " | ||
255 | "copied %d, capacity %ld\n", | ||
256 | rx_size, read_size, rx_skb->len, | ||
257 | (long) (skb_end_pointer(new_skb) - new_skb->head)); | ||
258 | goto retry; | ||
259 | } | ||
260 | /* In most cases, it happens due to the hardware scheduling a | ||
261 | * read when there was no data - unfortunately, we have no way | ||
262 | * to tell this timeout from a USB timeout. So we just ignore | ||
263 | * it. */ | ||
264 | case -ETIMEDOUT: | ||
265 | dev_err(dev, "RX: timeout: %d\n", result); | ||
266 | result = 0; | ||
267 | break; | ||
268 | default: /* Any error */ | ||
269 | if (edc_inc(&i2400mu->urb_edc, | ||
270 | EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) | ||
271 | goto error_reset; | ||
272 | dev_err(dev, "RX: error receiving URB: %d, retrying\n", result); | ||
273 | goto retry; | ||
274 | } | ||
275 | out: | ||
276 | if (do_autopm) | ||
277 | usb_autopm_put_interface(i2400mu->usb_iface); | ||
278 | d_fnend(4, dev, "(i2400mu %p) = %p\n", i2400mu, rx_skb); | ||
279 | return rx_skb; | ||
280 | |||
281 | error_reset: | ||
282 | dev_err(dev, "RX: maximum errors in URB exceeded; " | ||
283 | "resetting device\n"); | ||
284 | usb_queue_reset_device(i2400mu->usb_iface); | ||
285 | rx_skb = ERR_PTR(result); | ||
286 | goto out; | ||
287 | } | ||
288 | |||
289 | |||
290 | /* | ||
291 | * Kernel thread for USB reception of data | ||
292 | * | ||
293 | * This thread waits for a kick; once kicked, it will allocate an skb | ||
294 | * and receive a single message to it from USB (using | ||
295 | * i2400mu_rx()). Once received, it is passed to the generic i2400m RX | ||
296 | * code for processing. | ||
297 | * | ||
298 | * When done processing, it runs some dirty statistics to verify if | ||
299 | * the last 100 messages received were smaller than half of the | ||
300 | * current RX buffer size. In that case, the RX buffer size is | ||
301 | * halved. This will helps lowering the pressure on the memory | ||
302 | * allocator. | ||
303 | * | ||
304 | * Hard errors force the thread to exit. | ||
305 | */ | ||
306 | static | ||
307 | int i2400mu_rxd(void *_i2400mu) | ||
308 | { | ||
309 | int result = 0; | ||
310 | struct i2400mu *i2400mu = _i2400mu; | ||
311 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
312 | struct device *dev = &i2400mu->usb_iface->dev; | ||
313 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
314 | size_t pending; | ||
315 | int rx_size; | ||
316 | struct sk_buff *rx_skb; | ||
317 | |||
318 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); | ||
319 | while (1) { | ||
320 | d_printf(2, dev, "TX: waiting for messages\n"); | ||
321 | pending = 0; | ||
322 | wait_event_interruptible( | ||
323 | i2400mu->rx_wq, | ||
324 | (kthread_should_stop() /* check this first! */ | ||
325 | || (pending = atomic_read(&i2400mu->rx_pending_count))) | ||
326 | ); | ||
327 | if (kthread_should_stop()) | ||
328 | break; | ||
329 | if (pending == 0) | ||
330 | continue; | ||
331 | rx_size = i2400mu->rx_size; | ||
332 | d_printf(2, dev, "RX: reading up to %d bytes\n", rx_size); | ||
333 | rx_skb = __netdev_alloc_skb(net_dev, rx_size, GFP_KERNEL); | ||
334 | if (rx_skb == NULL) { | ||
335 | dev_err(dev, "RX: can't allocate skb [%d bytes]\n", | ||
336 | rx_size); | ||
337 | msleep(50); /* give it some time? */ | ||
338 | continue; | ||
339 | } | ||
340 | |||
341 | /* Receive the message with the payloads */ | ||
342 | rx_skb = i2400mu_rx(i2400mu, rx_skb); | ||
343 | result = PTR_ERR(rx_skb); | ||
344 | if (IS_ERR(rx_skb)) | ||
345 | goto out; | ||
346 | atomic_dec(&i2400mu->rx_pending_count); | ||
347 | if (rx_skb->len == 0) { /* some ignorable condition */ | ||
348 | kfree_skb(rx_skb); | ||
349 | continue; | ||
350 | } | ||
351 | |||
352 | /* Deliver the message to the generic i2400m code */ | ||
353 | i2400mu->rx_size_cnt++; | ||
354 | i2400mu->rx_size_acc += rx_skb->len; | ||
355 | result = i2400m_rx(i2400m, rx_skb); | ||
356 | if (result == -EIO | ||
357 | && edc_inc(&i2400mu->urb_edc, | ||
358 | EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { | ||
359 | goto error_reset; | ||
360 | } | ||
361 | |||
362 | /* Maybe adjust RX buffer size */ | ||
363 | i2400mu_rx_size_maybe_shrink(i2400mu); | ||
364 | } | ||
365 | result = 0; | ||
366 | out: | ||
367 | d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); | ||
368 | return result; | ||
369 | |||
370 | error_reset: | ||
371 | dev_err(dev, "RX: maximum errors in received buffer exceeded; " | ||
372 | "resetting device\n"); | ||
373 | usb_queue_reset_device(i2400mu->usb_iface); | ||
374 | goto out; | ||
375 | } | ||
376 | |||
377 | |||
378 | /* | ||
379 | * Start reading from the device | ||
380 | * | ||
381 | * @i2400m: device instance | ||
382 | * | ||
383 | * Notify the RX thread that there is data pending. | ||
384 | */ | ||
385 | void i2400mu_rx_kick(struct i2400mu *i2400mu) | ||
386 | { | ||
387 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
388 | struct device *dev = &i2400mu->usb_iface->dev; | ||
389 | |||
390 | d_fnstart(3, dev, "(i2400mu %p)\n", i2400m); | ||
391 | atomic_inc(&i2400mu->rx_pending_count); | ||
392 | wake_up_all(&i2400mu->rx_wq); | ||
393 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
394 | } | ||
395 | |||
396 | |||
397 | int i2400mu_rx_setup(struct i2400mu *i2400mu) | ||
398 | { | ||
399 | int result = 0; | ||
400 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
401 | struct device *dev = &i2400mu->usb_iface->dev; | ||
402 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
403 | |||
404 | i2400mu->rx_kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx", | ||
405 | wimax_dev->name); | ||
406 | if (IS_ERR(i2400mu->rx_kthread)) { | ||
407 | result = PTR_ERR(i2400mu->rx_kthread); | ||
408 | dev_err(dev, "RX: cannot start thread: %d\n", result); | ||
409 | } | ||
410 | return result; | ||
411 | } | ||
412 | |||
413 | void i2400mu_rx_release(struct i2400mu *i2400mu) | ||
414 | { | ||
415 | kthread_stop(i2400mu->rx_kthread); | ||
416 | } | ||
417 | |||
diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c new file mode 100644 index 000000000000..dfd893356f49 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-tx.c | |||
@@ -0,0 +1,229 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * USB specific TX handling | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * * Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * * Redistributions in binary form must reproduce the above copyright | ||
15 | * notice, this list of conditions and the following disclaimer in | ||
16 | * the documentation and/or other materials provided with the | ||
17 | * distribution. | ||
18 | * * Neither the name of Intel Corporation nor the names of its | ||
19 | * contributors may be used to endorse or promote products derived | ||
20 | * from this software without specific prior written permission. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
23 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
24 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
25 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
26 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
27 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
28 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
32 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
33 | * | ||
34 | * | ||
35 | * Intel Corporation <linux-wimax@intel.com> | ||
36 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
37 | * - Initial implementation | ||
38 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
39 | * - Split transport/device specific | ||
40 | * | ||
41 | * | ||
42 | * Takes the TX messages in the i2400m's driver TX FIFO and sends them | ||
43 | * to the device until there are no more. | ||
44 | * | ||
45 | * If we fail sending the message, we just drop it. There isn't much | ||
46 | * we can do at this point. We could also retry, but the USB stack has | ||
47 | * already retried and still failed, so there is not much of a | ||
48 | * point. As well, most of the traffic is network, which has recovery | ||
49 | * methods for dropped packets. | ||
50 | * | ||
51 | * For sending we just obtain a FIFO buffer to send, send it to the | ||
52 | * USB bulk out, tell the TX FIFO code we have sent it; query for | ||
53 | * another one, etc... until done. | ||
54 | * | ||
55 | * We use a thread so we can call usb_autopm_enable() and | ||
56 | * usb_autopm_disable() for each transaction; this way when the device | ||
57 | * goes idle, it will suspend. It also has less overhead than a | ||
58 | * dedicated workqueue, as it is being used for a single task. | ||
59 | * | ||
60 | * ROADMAP | ||
61 | * | ||
62 | * i2400mu_tx_setup() | ||
63 | * i2400mu_tx_release() | ||
64 | * | ||
65 | * i2400mu_bus_tx_kick() - Called by the tx.c code when there | ||
66 | * is new data in the FIFO. | ||
67 | * i2400mu_txd() | ||
68 | * i2400m_tx_msg_get() | ||
69 | * i2400m_tx_msg_sent() | ||
70 | */ | ||
71 | #include "i2400m-usb.h" | ||
72 | |||
73 | |||
74 | #define D_SUBMODULE tx | ||
75 | #include "usb-debug-levels.h" | ||
76 | |||
77 | |||
78 | /* | ||
79 | * Get the next TX message in the TX FIFO and send it to the device | ||
80 | * | ||
81 | * Note that any iteration consumes a message to be sent, no matter if | ||
82 | * it succeeds or fails (we have no real way to retry or complain). | ||
83 | * | ||
84 | * Return: 0 if ok, < 0 errno code on hard error. | ||
85 | */ | ||
86 | static | ||
87 | int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg, | ||
88 | size_t tx_msg_size) | ||
89 | { | ||
90 | int result = 0; | ||
91 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
92 | struct device *dev = &i2400mu->usb_iface->dev; | ||
93 | int usb_pipe, sent_size, do_autopm; | ||
94 | struct usb_endpoint_descriptor *epd; | ||
95 | |||
96 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); | ||
97 | do_autopm = atomic_read(&i2400mu->do_autopm); | ||
98 | result = do_autopm ? | ||
99 | usb_autopm_get_interface(i2400mu->usb_iface) : 0; | ||
100 | if (result < 0) { | ||
101 | dev_err(dev, "TX: can't get autopm: %d\n", result); | ||
102 | do_autopm = 0; | ||
103 | } | ||
104 | epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT); | ||
105 | usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); | ||
106 | retry: | ||
107 | result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe, | ||
108 | tx_msg, tx_msg_size, &sent_size, HZ); | ||
109 | usb_mark_last_busy(i2400mu->usb_dev); | ||
110 | switch (result) { | ||
111 | case 0: | ||
112 | if (sent_size != tx_msg_size) { /* Too short? drop it */ | ||
113 | dev_err(dev, "TX: short write (%d B vs %zu " | ||
114 | "expected)\n", sent_size, tx_msg_size); | ||
115 | result = -EIO; | ||
116 | } | ||
117 | break; | ||
118 | case -EINVAL: /* while removing driver */ | ||
119 | case -ENODEV: /* dev disconnect ... */ | ||
120 | case -ENOENT: /* just ignore it */ | ||
121 | case -ESHUTDOWN: /* and exit */ | ||
122 | case -ECONNRESET: | ||
123 | result = -ESHUTDOWN; | ||
124 | break; | ||
125 | default: /* Some error? */ | ||
126 | if (edc_inc(&i2400mu->urb_edc, | ||
127 | EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { | ||
128 | dev_err(dev, "TX: maximum errors in URB " | ||
129 | "exceeded; resetting device\n"); | ||
130 | usb_queue_reset_device(i2400mu->usb_iface); | ||
131 | } else { | ||
132 | dev_err(dev, "TX: cannot send URB; retrying. " | ||
133 | "tx_msg @%zu %zu B [%d sent]: %d\n", | ||
134 | (void *) tx_msg - i2400m->tx_buf, | ||
135 | tx_msg_size, sent_size, result); | ||
136 | goto retry; | ||
137 | } | ||
138 | } | ||
139 | if (do_autopm) | ||
140 | usb_autopm_put_interface(i2400mu->usb_iface); | ||
141 | d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu); | ||
142 | return result; | ||
143 | } | ||
144 | |||
145 | |||
146 | /* | ||
147 | * Get the next TX message in the TX FIFO and send it to the device | ||
148 | * | ||
149 | * Note we exit the loop if i2400mu_tx() fails; that funtion only | ||
150 | * fails on hard error (failing to tx a buffer not being one of them, | ||
151 | * see its doc). | ||
152 | * | ||
153 | * Return: 0 | ||
154 | */ | ||
155 | static | ||
156 | int i2400mu_txd(void *_i2400mu) | ||
157 | { | ||
158 | int result = 0; | ||
159 | struct i2400mu *i2400mu = _i2400mu; | ||
160 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
161 | struct device *dev = &i2400mu->usb_iface->dev; | ||
162 | struct i2400m_msg_hdr *tx_msg; | ||
163 | size_t tx_msg_size; | ||
164 | |||
165 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); | ||
166 | |||
167 | while (1) { | ||
168 | d_printf(2, dev, "TX: waiting for messages\n"); | ||
169 | tx_msg = NULL; | ||
170 | wait_event_interruptible( | ||
171 | i2400mu->tx_wq, | ||
172 | (kthread_should_stop() /* check this first! */ | ||
173 | || (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size))) | ||
174 | ); | ||
175 | if (kthread_should_stop()) | ||
176 | break; | ||
177 | WARN_ON(tx_msg == NULL); /* should not happen...*/ | ||
178 | d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size); | ||
179 | d_dump(5, dev, tx_msg, tx_msg_size); | ||
180 | /* Yeah, we ignore errors ... not much we can do */ | ||
181 | i2400mu_tx(i2400mu, tx_msg, tx_msg_size); | ||
182 | i2400m_tx_msg_sent(i2400m); /* ack it, advance the FIFO */ | ||
183 | if (result < 0) | ||
184 | break; | ||
185 | } | ||
186 | d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); | ||
187 | return result; | ||
188 | } | ||
189 | |||
190 | |||
191 | /* | ||
192 | * i2400m TX engine notifies us that there is data in the FIFO ready | ||
193 | * for TX | ||
194 | * | ||
195 | * If there is a URB in flight, don't do anything; when it finishes, | ||
196 | * it will see there is data in the FIFO and send it. Else, just | ||
197 | * submit a write. | ||
198 | */ | ||
199 | void i2400mu_bus_tx_kick(struct i2400m *i2400m) | ||
200 | { | ||
201 | struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); | ||
202 | struct device *dev = &i2400mu->usb_iface->dev; | ||
203 | |||
204 | d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m); | ||
205 | wake_up_all(&i2400mu->tx_wq); | ||
206 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
207 | } | ||
208 | |||
209 | |||
210 | int i2400mu_tx_setup(struct i2400mu *i2400mu) | ||
211 | { | ||
212 | int result = 0; | ||
213 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
214 | struct device *dev = &i2400mu->usb_iface->dev; | ||
215 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | ||
216 | |||
217 | i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", | ||
218 | wimax_dev->name); | ||
219 | if (IS_ERR(i2400mu->tx_kthread)) { | ||
220 | result = PTR_ERR(i2400mu->tx_kthread); | ||
221 | dev_err(dev, "TX: cannot start thread: %d\n", result); | ||
222 | } | ||
223 | return result; | ||
224 | } | ||
225 | |||
226 | void i2400mu_tx_release(struct i2400mu *i2400mu) | ||
227 | { | ||
228 | kthread_stop(i2400mu->tx_kthread); | ||
229 | } | ||
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c new file mode 100644 index 000000000000..6d4b65fd9c17 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb.c | |||
@@ -0,0 +1,591 @@ | |||
1 | /* | ||
2 | * Intel Wireless WiMAX Connection 2400m | ||
3 | * Linux driver model glue for USB device, reset & fw upload | ||
4 | * | ||
5 | * | ||
6 | * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> | ||
7 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | ||
8 | * Yanir Lubetkin <yanirx.lubetkin@intel.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License version | ||
12 | * 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
22 | * 02110-1301, USA. | ||
23 | * | ||
24 | * | ||
25 | * See i2400m-usb.h for a general description of this driver. | ||
26 | * | ||
27 | * This file implements driver model glue, and hook ups for the | ||
28 | * generic driver to implement the bus-specific functions (device | ||
29 | * communication setup/tear down, firmware upload and resetting). | ||
30 | * | ||
31 | * ROADMAP | ||
32 | * | ||
33 | * i2400mu_probe() | ||
34 | * alloc_netdev()... | ||
35 | * i2400mu_netdev_setup() | ||
36 | * i2400mu_init() | ||
37 | * i2400m_netdev_setup() | ||
38 | * i2400m_setup()... | ||
39 | * | ||
40 | * i2400mu_disconnect | ||
41 | * i2400m_release() | ||
42 | * free_netdev() | ||
43 | * | ||
44 | * i2400mu_suspend() | ||
45 | * i2400m_cmd_enter_powersave() | ||
46 | * i2400mu_notification_release() | ||
47 | * | ||
48 | * i2400mu_resume() | ||
49 | * i2400mu_notification_setup() | ||
50 | * | ||
51 | * i2400mu_bus_dev_start() Called by i2400m_dev_start() [who is | ||
52 | * i2400mu_tx_setup() called by i2400m_setup()] | ||
53 | * i2400mu_rx_setup() | ||
54 | * i2400mu_notification_setup() | ||
55 | * | ||
56 | * i2400mu_bus_dev_stop() Called by i2400m_dev_stop() [who is | ||
57 | * i2400mu_notification_release() called by i2400m_release()] | ||
58 | * i2400mu_rx_release() | ||
59 | * i2400mu_tx_release() | ||
60 | * | ||
61 | * i2400mu_bus_reset() Called by i2400m->bus_reset | ||
62 | * __i2400mu_reset() | ||
63 | * __i2400mu_send_barker() | ||
64 | * usb_reset_device() | ||
65 | */ | ||
66 | #include "i2400m-usb.h" | ||
67 | #include <linux/wimax/i2400m.h> | ||
68 | #include <linux/debugfs.h> | ||
69 | |||
70 | |||
71 | #define D_SUBMODULE usb | ||
72 | #include "usb-debug-levels.h" | ||
73 | |||
74 | |||
75 | /* Our firmware file name */ | ||
76 | #define I2400MU_FW_FILE_NAME "i2400m-fw-usb-" I2400M_FW_VERSION ".sbcf" | ||
77 | |||
78 | static | ||
79 | int i2400mu_bus_dev_start(struct i2400m *i2400m) | ||
80 | { | ||
81 | int result; | ||
82 | struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); | ||
83 | struct device *dev = &i2400mu->usb_iface->dev; | ||
84 | |||
85 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
86 | result = i2400mu_tx_setup(i2400mu); | ||
87 | if (result < 0) | ||
88 | goto error_usb_tx_setup; | ||
89 | result = i2400mu_rx_setup(i2400mu); | ||
90 | if (result < 0) | ||
91 | goto error_usb_rx_setup; | ||
92 | result = i2400mu_notification_setup(i2400mu); | ||
93 | if (result < 0) | ||
94 | goto error_notif_setup; | ||
95 | d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); | ||
96 | return result; | ||
97 | |||
98 | error_notif_setup: | ||
99 | i2400mu_rx_release(i2400mu); | ||
100 | error_usb_rx_setup: | ||
101 | i2400mu_tx_release(i2400mu); | ||
102 | error_usb_tx_setup: | ||
103 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
104 | return result; | ||
105 | } | ||
106 | |||
107 | |||
108 | static | ||
109 | void i2400mu_bus_dev_stop(struct i2400m *i2400m) | ||
110 | { | ||
111 | struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); | ||
112 | struct device *dev = &i2400mu->usb_iface->dev; | ||
113 | |||
114 | d_fnstart(3, dev, "(i2400m %p)\n", i2400m); | ||
115 | i2400mu_notification_release(i2400mu); | ||
116 | i2400mu_rx_release(i2400mu); | ||
117 | i2400mu_tx_release(i2400mu); | ||
118 | d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); | ||
119 | } | ||
120 | |||
121 | |||
122 | /* | ||
123 | * Sends a barker buffer to the device | ||
124 | * | ||
125 | * This helper will allocate a kmalloced buffer and use it to transmit | ||
126 | * (then free it). Reason for this is that other arches cannot use | ||
127 | * stack/vmalloc/text areas for DMA transfers. | ||
128 | * | ||
129 | * Error recovery here is simpler: anything is considered a hard error | ||
130 | * and will move the reset code to use a last-resort bus-based reset. | ||
131 | */ | ||
132 | static | ||
133 | int __i2400mu_send_barker(struct i2400mu *i2400mu, | ||
134 | const __le32 *barker, | ||
135 | size_t barker_size, | ||
136 | unsigned endpoint) | ||
137 | { | ||
138 | struct usb_endpoint_descriptor *epd = NULL; | ||
139 | int pipe, actual_len, ret; | ||
140 | struct device *dev = &i2400mu->usb_iface->dev; | ||
141 | void *buffer; | ||
142 | int do_autopm = 1; | ||
143 | |||
144 | ret = usb_autopm_get_interface(i2400mu->usb_iface); | ||
145 | if (ret < 0) { | ||
146 | dev_err(dev, "RESET: can't get autopm: %d\n", ret); | ||
147 | do_autopm = 0; | ||
148 | } | ||
149 | ret = -ENOMEM; | ||
150 | buffer = kmalloc(barker_size, GFP_KERNEL); | ||
151 | if (buffer == NULL) | ||
152 | goto error_kzalloc; | ||
153 | epd = usb_get_epd(i2400mu->usb_iface, endpoint); | ||
154 | pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); | ||
155 | memcpy(buffer, barker, barker_size); | ||
156 | ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size, | ||
157 | &actual_len, HZ); | ||
158 | if (ret < 0) { | ||
159 | if (ret != -EINVAL) | ||
160 | dev_err(dev, "E: barker error: %d\n", ret); | ||
161 | } else if (actual_len != barker_size) { | ||
162 | dev_err(dev, "E: only %d bytes transmitted\n", actual_len); | ||
163 | ret = -EIO; | ||
164 | } | ||
165 | kfree(buffer); | ||
166 | error_kzalloc: | ||
167 | if (do_autopm) | ||
168 | usb_autopm_put_interface(i2400mu->usb_iface); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | |||
173 | /* | ||
174 | * Reset a device at different levels (warm, cold or bus) | ||
175 | * | ||
176 | * @i2400m: device descriptor | ||
177 | * @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS) | ||
178 | * | ||
179 | * Warm and cold resets get a USB reset if they fail. | ||
180 | * | ||
181 | * Warm reset: | ||
182 | * | ||
183 | * The device will be fully reset internally, but won't be | ||
184 | * disconnected from the USB bus (so no reenumeration will | ||
185 | * happen). Firmware upload will be neccessary. | ||
186 | * | ||
187 | * The device will send a reboot barker in the notification endpoint | ||
188 | * that will trigger the driver to reinitialize the state | ||
189 | * automatically from notif.c:i2400m_notification_grok() into | ||
190 | * i2400m_dev_bootstrap_delayed(). | ||
191 | * | ||
192 | * Cold and bus (USB) reset: | ||
193 | * | ||
194 | * The device will be fully reset internally, disconnected from the | ||
195 | * USB bus an a reenumeration will happen. Firmware upload will be | ||
196 | * neccessary. Thus, we don't do any locking or struct | ||
197 | * reinitialization, as we are going to be fully disconnected and | ||
198 | * reenumerated. | ||
199 | * | ||
200 | * Note we need to return -ENODEV if a warm reset was requested and we | ||
201 | * had to resort to a bus reset. See i2400m_op_reset(), wimax_reset() | ||
202 | * and wimax_dev->op_reset. | ||
203 | * | ||
204 | * WARNING: no driver state saved/fixed | ||
205 | */ | ||
206 | static | ||
207 | int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) | ||
208 | { | ||
209 | int result; | ||
210 | struct i2400mu *i2400mu = | ||
211 | container_of(i2400m, struct i2400mu, i2400m); | ||
212 | struct device *dev = i2400m_dev(i2400m); | ||
213 | static const __le32 i2400m_WARM_BOOT_BARKER[4] = { | ||
214 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
215 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
216 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
217 | __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), | ||
218 | }; | ||
219 | static const __le32 i2400m_COLD_BOOT_BARKER[4] = { | ||
220 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
221 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
222 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
223 | __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), | ||
224 | }; | ||
225 | |||
226 | d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt); | ||
227 | if (rt == I2400M_RT_WARM) | ||
228 | result = __i2400mu_send_barker(i2400mu, i2400m_WARM_BOOT_BARKER, | ||
229 | sizeof(i2400m_WARM_BOOT_BARKER), | ||
230 | I2400MU_EP_BULK_OUT); | ||
231 | else if (rt == I2400M_RT_COLD) | ||
232 | result = __i2400mu_send_barker(i2400mu, i2400m_COLD_BOOT_BARKER, | ||
233 | sizeof(i2400m_COLD_BOOT_BARKER), | ||
234 | I2400MU_EP_RESET_COLD); | ||
235 | else if (rt == I2400M_RT_BUS) { | ||
236 | do_bus_reset: | ||
237 | result = usb_reset_device(i2400mu->usb_dev); | ||
238 | switch (result) { | ||
239 | case 0: | ||
240 | case -EINVAL: /* device is gone */ | ||
241 | case -ENODEV: | ||
242 | case -ENOENT: | ||
243 | case -ESHUTDOWN: | ||
244 | result = rt == I2400M_RT_WARM ? -ENODEV : 0; | ||
245 | break; /* We assume the device is disconnected */ | ||
246 | default: | ||
247 | dev_err(dev, "USB reset failed (%d), giving up!\n", | ||
248 | result); | ||
249 | } | ||
250 | } else | ||
251 | BUG(); | ||
252 | if (result < 0 | ||
253 | && result != -EINVAL /* device is gone */ | ||
254 | && rt != I2400M_RT_BUS) { | ||
255 | dev_err(dev, "%s reset failed (%d); trying USB reset\n", | ||
256 | rt == I2400M_RT_WARM ? "warm" : "cold", result); | ||
257 | rt = I2400M_RT_BUS; | ||
258 | goto do_bus_reset; | ||
259 | } | ||
260 | d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); | ||
261 | return result; | ||
262 | } | ||
263 | |||
264 | |||
265 | static | ||
266 | void i2400mu_netdev_setup(struct net_device *net_dev) | ||
267 | { | ||
268 | struct i2400m *i2400m = net_dev_to_i2400m(net_dev); | ||
269 | struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); | ||
270 | i2400mu_init(i2400mu); | ||
271 | i2400m_netdev_setup(net_dev); | ||
272 | } | ||
273 | |||
274 | |||
275 | /* | ||
276 | * Debug levels control; see debug.h | ||
277 | */ | ||
278 | struct d_level D_LEVEL[] = { | ||
279 | D_SUBMODULE_DEFINE(usb), | ||
280 | D_SUBMODULE_DEFINE(fw), | ||
281 | D_SUBMODULE_DEFINE(notif), | ||
282 | D_SUBMODULE_DEFINE(rx), | ||
283 | D_SUBMODULE_DEFINE(tx), | ||
284 | }; | ||
285 | size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); | ||
286 | |||
287 | |||
288 | #define __debugfs_register(prefix, name, parent) \ | ||
289 | do { \ | ||
290 | result = d_level_register_debugfs(prefix, name, parent); \ | ||
291 | if (result < 0) \ | ||
292 | goto error; \ | ||
293 | } while (0) | ||
294 | |||
295 | |||
296 | static | ||
297 | int i2400mu_debugfs_add(struct i2400mu *i2400mu) | ||
298 | { | ||
299 | int result; | ||
300 | struct device *dev = &i2400mu->usb_iface->dev; | ||
301 | struct dentry *dentry = i2400mu->i2400m.wimax_dev.debugfs_dentry; | ||
302 | struct dentry *fd; | ||
303 | |||
304 | dentry = debugfs_create_dir("i2400m-usb", dentry); | ||
305 | result = PTR_ERR(dentry); | ||
306 | if (IS_ERR(dentry)) { | ||
307 | if (result == -ENODEV) | ||
308 | result = 0; /* No debugfs support */ | ||
309 | goto error; | ||
310 | } | ||
311 | i2400mu->debugfs_dentry = dentry; | ||
312 | __debugfs_register("dl_", usb, dentry); | ||
313 | __debugfs_register("dl_", fw, dentry); | ||
314 | __debugfs_register("dl_", notif, dentry); | ||
315 | __debugfs_register("dl_", rx, dentry); | ||
316 | __debugfs_register("dl_", tx, dentry); | ||
317 | |||
318 | /* Don't touch these if you don't know what you are doing */ | ||
319 | fd = debugfs_create_u8("rx_size_auto_shrink", 0600, dentry, | ||
320 | &i2400mu->rx_size_auto_shrink); | ||
321 | result = PTR_ERR(fd); | ||
322 | if (IS_ERR(fd) && result != -ENODEV) { | ||
323 | dev_err(dev, "Can't create debugfs entry " | ||
324 | "rx_size_auto_shrink: %d\n", result); | ||
325 | goto error; | ||
326 | } | ||
327 | |||
328 | fd = debugfs_create_size_t("rx_size", 0600, dentry, | ||
329 | &i2400mu->rx_size); | ||
330 | result = PTR_ERR(fd); | ||
331 | if (IS_ERR(fd) && result != -ENODEV) { | ||
332 | dev_err(dev, "Can't create debugfs entry " | ||
333 | "rx_size: %d\n", result); | ||
334 | goto error; | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | |||
339 | error: | ||
340 | debugfs_remove_recursive(i2400mu->debugfs_dentry); | ||
341 | return result; | ||
342 | } | ||
343 | |||
344 | |||
345 | /* | ||
346 | * Probe a i2400m interface and register it | ||
347 | * | ||
348 | * @iface: USB interface to link to | ||
349 | * @id: USB class/subclass/protocol id | ||
350 | * @returns: 0 if ok, < 0 errno code on error. | ||
351 | * | ||
352 | * Alloc a net device, initialize the bus-specific details and then | ||
353 | * calls the bus-generic initialization routine. That will register | ||
354 | * the wimax and netdev devices, upload the firmware [using | ||
355 | * _bus_bm_*()], call _bus_dev_start() to finalize the setup of the | ||
356 | * communication with the device and then will start to talk to it to | ||
357 | * finnish setting it up. | ||
358 | */ | ||
359 | static | ||
360 | int i2400mu_probe(struct usb_interface *iface, | ||
361 | const struct usb_device_id *id) | ||
362 | { | ||
363 | int result; | ||
364 | struct net_device *net_dev; | ||
365 | struct device *dev = &iface->dev; | ||
366 | struct i2400m *i2400m; | ||
367 | struct i2400mu *i2400mu; | ||
368 | struct usb_device *usb_dev = interface_to_usbdev(iface); | ||
369 | |||
370 | if (usb_dev->speed != USB_SPEED_HIGH) | ||
371 | dev_err(dev, "device not connected as high speed\n"); | ||
372 | |||
373 | /* Allocate instance [calls i2400m_netdev_setup() on it]. */ | ||
374 | result = -ENOMEM; | ||
375 | net_dev = alloc_netdev(sizeof(*i2400mu), "wmx%d", | ||
376 | i2400mu_netdev_setup); | ||
377 | if (net_dev == NULL) { | ||
378 | dev_err(dev, "no memory for network device instance\n"); | ||
379 | goto error_alloc_netdev; | ||
380 | } | ||
381 | SET_NETDEV_DEV(net_dev, dev); | ||
382 | i2400m = net_dev_to_i2400m(net_dev); | ||
383 | i2400mu = container_of(i2400m, struct i2400mu, i2400m); | ||
384 | i2400m->wimax_dev.net_dev = net_dev; | ||
385 | i2400mu->usb_dev = usb_get_dev(usb_dev); | ||
386 | i2400mu->usb_iface = iface; | ||
387 | usb_set_intfdata(iface, i2400mu); | ||
388 | |||
389 | i2400m->bus_tx_block_size = I2400MU_BLK_SIZE; | ||
390 | i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX; | ||
391 | i2400m->bus_dev_start = i2400mu_bus_dev_start; | ||
392 | i2400m->bus_dev_stop = i2400mu_bus_dev_stop; | ||
393 | i2400m->bus_tx_kick = i2400mu_bus_tx_kick; | ||
394 | i2400m->bus_reset = i2400mu_bus_reset; | ||
395 | i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; | ||
396 | i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; | ||
397 | i2400m->bus_fw_name = I2400MU_FW_FILE_NAME; | ||
398 | i2400m->bus_bm_mac_addr_impaired = 0; | ||
399 | |||
400 | iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */ | ||
401 | device_init_wakeup(dev, 1); | ||
402 | usb_autopm_enable(i2400mu->usb_iface); | ||
403 | usb_dev->autosuspend_delay = 15 * HZ; | ||
404 | usb_dev->autosuspend_disabled = 0; | ||
405 | |||
406 | result = i2400m_setup(i2400m, I2400M_BRI_MAC_REINIT); | ||
407 | if (result < 0) { | ||
408 | dev_err(dev, "cannot setup device: %d\n", result); | ||
409 | goto error_setup; | ||
410 | } | ||
411 | result = i2400mu_debugfs_add(i2400mu); | ||
412 | if (result < 0) { | ||
413 | dev_err(dev, "Can't register i2400mu's debugfs: %d\n", result); | ||
414 | goto error_debugfs_add; | ||
415 | } | ||
416 | return 0; | ||
417 | |||
418 | error_debugfs_add: | ||
419 | i2400m_release(i2400m); | ||
420 | error_setup: | ||
421 | usb_set_intfdata(iface, NULL); | ||
422 | usb_put_dev(i2400mu->usb_dev); | ||
423 | free_netdev(net_dev); | ||
424 | error_alloc_netdev: | ||
425 | return result; | ||
426 | } | ||
427 | |||
428 | |||
429 | /* | ||
430 | * Disconect a i2400m from the system. | ||
431 | * | ||
432 | * i2400m_stop() has been called before, so al the rx and tx contexts | ||
433 | * have been taken down already. Make sure the queue is stopped, | ||
434 | * unregister netdev and i2400m, free and kill. | ||
435 | */ | ||
436 | static | ||
437 | void i2400mu_disconnect(struct usb_interface *iface) | ||
438 | { | ||
439 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
440 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
441 | struct net_device *net_dev = i2400m->wimax_dev.net_dev; | ||
442 | struct device *dev = &iface->dev; | ||
443 | |||
444 | d_fnstart(3, dev, "(iface %p i2400m %p)\n", iface, i2400m); | ||
445 | |||
446 | debugfs_remove_recursive(i2400mu->debugfs_dentry); | ||
447 | i2400m_release(i2400m); | ||
448 | usb_set_intfdata(iface, NULL); | ||
449 | usb_put_dev(i2400mu->usb_dev); | ||
450 | free_netdev(net_dev); | ||
451 | d_fnend(3, dev, "(iface %p i2400m %p) = void\n", iface, i2400m); | ||
452 | } | ||
453 | |||
454 | |||
455 | /* | ||
456 | * Get the device ready for USB port or system standby and hibernation | ||
457 | * | ||
458 | * USB port and system standby are handled the same. | ||
459 | * | ||
460 | * When the system hibernates, the USB device is powered down and then | ||
461 | * up, so we don't really have to do much here, as it will be seen as | ||
462 | * a reconnect. Still for simplicity we consider this case the same as | ||
463 | * suspend, so that the device has a chance to do notify the base | ||
464 | * station (if connected). | ||
465 | * | ||
466 | * So at the end, the three cases require common handling. | ||
467 | * | ||
468 | * If at the time of this call the device's firmware is not loaded, | ||
469 | * nothing has to be done. | ||
470 | * | ||
471 | * If the firmware is loaded, we need to: | ||
472 | * | ||
473 | * - tell the device to go into host interface power save mode, wait | ||
474 | * for it to ack | ||
475 | * | ||
476 | * This is quite more interesting than it is; we need to execute a | ||
477 | * command, but this time, we don't want the code in usb-{tx,rx}.c | ||
478 | * to call the usb_autopm_get/put_interface() barriers as it'd | ||
479 | * deadlock, so we need to decrement i2400mu->do_autopm, that acts | ||
480 | * as a poor man's semaphore. Ugly, but it works. | ||
481 | * | ||
482 | * As well, the device might refuse going to sleep for whichever | ||
483 | * reason. In this case we just fail. For system suspend/hibernate, | ||
484 | * we *can't* fail. We look at usb_dev->auto_pm to see if the | ||
485 | * suspend call comes from the USB stack or from the system and act | ||
486 | * in consequence. | ||
487 | * | ||
488 | * - stop the notification endpoint polling | ||
489 | */ | ||
490 | static | ||
491 | int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) | ||
492 | { | ||
493 | int result = 0; | ||
494 | struct device *dev = &iface->dev; | ||
495 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
496 | struct usb_device *usb_dev = i2400mu->usb_dev; | ||
497 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
498 | |||
499 | d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); | ||
500 | if (i2400m->updown == 0) | ||
501 | goto no_firmware; | ||
502 | d_printf(1, dev, "fw up, requesting standby\n"); | ||
503 | atomic_dec(&i2400mu->do_autopm); | ||
504 | result = i2400m_cmd_enter_powersave(i2400m); | ||
505 | atomic_inc(&i2400mu->do_autopm); | ||
506 | if (result < 0 && usb_dev->auto_pm == 0) { | ||
507 | /* System suspend, can't fail */ | ||
508 | dev_err(dev, "failed to suspend, will reset on resume\n"); | ||
509 | result = 0; | ||
510 | } | ||
511 | if (result < 0) | ||
512 | goto error_enter_powersave; | ||
513 | i2400mu_notification_release(i2400mu); | ||
514 | d_printf(1, dev, "fw up, got standby\n"); | ||
515 | error_enter_powersave: | ||
516 | no_firmware: | ||
517 | d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", | ||
518 | iface, pm_msg.event, result); | ||
519 | return result; | ||
520 | } | ||
521 | |||
522 | |||
523 | static | ||
524 | int i2400mu_resume(struct usb_interface *iface) | ||
525 | { | ||
526 | int ret = 0; | ||
527 | struct device *dev = &iface->dev; | ||
528 | struct i2400mu *i2400mu = usb_get_intfdata(iface); | ||
529 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
530 | |||
531 | d_fnstart(3, dev, "(iface %p)\n", iface); | ||
532 | if (i2400m->updown == 0) { | ||
533 | d_printf(1, dev, "fw was down, no resume neeed\n"); | ||
534 | goto out; | ||
535 | } | ||
536 | d_printf(1, dev, "fw was up, resuming\n"); | ||
537 | i2400mu_notification_setup(i2400mu); | ||
538 | /* USB has flow control, so we don't need to give it time to | ||
539 | * come back; otherwise, we'd use something like a get-state | ||
540 | * command... */ | ||
541 | out: | ||
542 | d_fnend(3, dev, "(iface %p) = %d\n", iface, ret); | ||
543 | return ret; | ||
544 | } | ||
545 | |||
546 | |||
547 | static | ||
548 | struct usb_device_id i2400mu_id_table[] = { | ||
549 | { USB_DEVICE(0x8086, 0x0181) }, | ||
550 | { USB_DEVICE(0x8086, 0x1403) }, | ||
551 | { USB_DEVICE(0x8086, 0x1405) }, | ||
552 | { USB_DEVICE(0x8086, 0x0180) }, | ||
553 | { USB_DEVICE(0x8086, 0x0182) }, | ||
554 | { USB_DEVICE(0x8086, 0x1406) }, | ||
555 | { USB_DEVICE(0x8086, 0x1403) }, | ||
556 | { }, | ||
557 | }; | ||
558 | MODULE_DEVICE_TABLE(usb, i2400mu_id_table); | ||
559 | |||
560 | |||
561 | static | ||
562 | struct usb_driver i2400mu_driver = { | ||
563 | .name = KBUILD_MODNAME, | ||
564 | .suspend = i2400mu_suspend, | ||
565 | .resume = i2400mu_resume, | ||
566 | .probe = i2400mu_probe, | ||
567 | .disconnect = i2400mu_disconnect, | ||
568 | .id_table = i2400mu_id_table, | ||
569 | .supports_autosuspend = 1, | ||
570 | }; | ||
571 | |||
572 | static | ||
573 | int __init i2400mu_driver_init(void) | ||
574 | { | ||
575 | return usb_register(&i2400mu_driver); | ||
576 | } | ||
577 | module_init(i2400mu_driver_init); | ||
578 | |||
579 | |||
580 | static | ||
581 | void __exit i2400mu_driver_exit(void) | ||
582 | { | ||
583 | flush_scheduled_work(); /* for the stuff we schedule from sysfs.c */ | ||
584 | usb_deregister(&i2400mu_driver); | ||
585 | } | ||
586 | module_exit(i2400mu_driver_exit); | ||
587 | |||
588 | MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); | ||
589 | MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB"); | ||
590 | MODULE_LICENSE("GPL"); | ||
591 | MODULE_FIRMWARE(I2400MU_FW_FILE_NAME); | ||