Commit | Line | Data |
---|---|---|
11a7d0e3 IPG |
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 | ||
2093586d DB |
91 | struct i2400m_endpoint_cfg { |
92 | unsigned char bulk_out; | |
93 | unsigned char notification; | |
94 | unsigned char reset_cold; | |
95 | unsigned char bulk_in; | |
96 | }; | |
97 | ||
11a7d0e3 IPG |
98 | static inline void edc_init(struct edc *edc) |
99 | { | |
100 | edc->timestart = jiffies; | |
101 | } | |
102 | ||
103 | /** | |
104 | * edc_inc - report a soft error and check if we are over the watermark | |
105 | * | |
106 | * @edc: pointer to error density counter. | |
107 | * @max_err: maximum number of errors we can accept over the timeframe | |
108 | * @timeframe: lenght of the timeframe (in jiffies). | |
109 | * | |
110 | * Returns: !0 1 if maximum acceptable errors per timeframe has been | |
111 | * exceeded. 0 otherwise. | |
112 | * | |
113 | * This is way to determine if the number of acceptable errors per time | |
114 | * period has been exceeded. It is not accurate as there are cases in which | |
115 | * this scheme will not work, for example if there are periodic occurences | |
116 | * of errors that straddle updates to the start time. This scheme is | |
117 | * sufficient for our usage. | |
118 | * | |
119 | * To use, embed a 'struct edc' somewhere, initialize it with | |
120 | * edc_init() and when an error hits: | |
121 | * | |
122 | * if (do_something_fails_with_a_soft_error) { | |
123 | * if (edc_inc(&my->edc, MAX_ERRORS, MAX_TIMEFRAME)) | |
124 | * Ops, hard error, do something about it | |
125 | * else | |
126 | * Retry or ignore, depending on whatever | |
127 | * } | |
128 | */ | |
129 | static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe) | |
130 | { | |
131 | unsigned long now; | |
132 | ||
133 | now = jiffies; | |
134 | if (now - edc->timestart > timeframe) { | |
135 | edc->errorcount = 1; | |
136 | edc->timestart = now; | |
137 | } else if (++edc->errorcount > max_err) { | |
138 | edc->errorcount = 0; | |
139 | edc->timestart = now; | |
140 | return 1; | |
141 | } | |
142 | return 0; | |
143 | } | |
144 | ||
145 | /* Host-Device interface for USB */ | |
146 | enum { | |
c3083658 | 147 | I2400M_USB_BOOT_RETRIES = 3, |
11a7d0e3 IPG |
148 | I2400MU_MAX_NOTIFICATION_LEN = 256, |
149 | I2400MU_BLK_SIZE = 16, | |
150 | I2400MU_PL_SIZE_MAX = 0x3EFF, | |
7329012e DB |
151 | |
152 | /* Device IDs */ | |
153 | USB_DEVICE_ID_I6050 = 0x0186, | |
b8112469 | 154 | USB_DEVICE_ID_I6050_2 = 0x0188, |
41a8730c | 155 | USB_DEVICE_ID_I6250 = 0x0187, |
11a7d0e3 IPG |
156 | }; |
157 | ||
158 | ||
159 | /** | |
160 | * struct i2400mu - descriptor for a USB connected i2400m | |
161 | * | |
162 | * @i2400m: bus-generic i2400m implementation; has to be first (see | |
163 | * it's documentation in i2400m.h). | |
164 | * | |
165 | * @usb_dev: pointer to our USB device | |
166 | * | |
167 | * @usb_iface: pointer to our USB interface | |
168 | * | |
169 | * @urb_edc: error density counter; used to keep a density-on-time tab | |
170 | * on how many soft (retryable or ignorable) errors we get. If we | |
171 | * go over the threshold, we consider the bus transport is failing | |
172 | * too much and reset. | |
173 | * | |
174 | * @notif_urb: URB for receiving notifications from the device. | |
175 | * | |
176 | * @tx_kthread: thread we use for data TX. We use a thread because in | |
177 | * order to do deep power saving and put the device to sleep, we | |
178 | * need to call usb_autopm_*() [blocking functions]. | |
179 | * | |
180 | * @tx_wq: waitqueue for the TX kthread to sleep when there is no data | |
181 | * to be sent; when more data is available, it is woken up by | |
182 | * i2400mu_bus_tx_kick(). | |
183 | * | |
184 | * @rx_kthread: thread we use for data RX. We use a thread because in | |
185 | * order to do deep power saving and put the device to sleep, we | |
186 | * need to call usb_autopm_*() [blocking functions]. | |
187 | * | |
188 | * @rx_wq: waitqueue for the RX kthread to sleep when there is no data | |
189 | * to receive. When data is available, it is woken up by | |
190 | * usb-notif.c:i2400mu_notification_grok(). | |
191 | * | |
192 | * @rx_pending_count: number of rx-data-ready notifications that were | |
193 | * still not handled by the RX kthread. | |
194 | * | |
195 | * @rx_size: current RX buffer size that is being used. | |
196 | * | |
197 | * @rx_size_acc: accumulator of the sizes of the previous read | |
198 | * transactions. | |
199 | * | |
200 | * @rx_size_cnt: number of read transactions accumulated in | |
201 | * @rx_size_acc. | |
202 | * | |
203 | * @do_autopm: disable(0)/enable(>0) calling the | |
204 | * usb_autopm_get/put_interface() barriers when executing | |
205 | * commands. See doc in i2400mu_suspend() for more information. | |
206 | * | |
207 | * @rx_size_auto_shrink: if true, the rx_size is shrinked | |
208 | * automatically based on the average size of the received | |
209 | * transactions. This allows the receive code to allocate smaller | |
210 | * chunks of memory and thus reduce pressure on the memory | |
211 | * allocator by not wasting so much space. By default it is | |
212 | * enabled. | |
213 | * | |
214 | * @debugfs_dentry: hookup for debugfs files. | |
215 | * These have to be in a separate directory, a child of | |
216 | * (wimax_dev->debugfs_dentry) so they can be removed when the | |
217 | * module unloads, as we don't keep each dentry. | |
218 | */ | |
219 | struct i2400mu { | |
220 | struct i2400m i2400m; /* FIRST! See doc */ | |
221 | ||
222 | struct usb_device *usb_dev; | |
223 | struct usb_interface *usb_iface; | |
224 | struct edc urb_edc; /* Error density counter */ | |
2093586d | 225 | struct i2400m_endpoint_cfg endpoint_cfg; |
11a7d0e3 IPG |
226 | |
227 | struct urb *notif_urb; | |
228 | struct task_struct *tx_kthread; | |
229 | wait_queue_head_t tx_wq; | |
230 | ||
231 | struct task_struct *rx_kthread; | |
232 | wait_queue_head_t rx_wq; | |
233 | atomic_t rx_pending_count; | |
234 | size_t rx_size, rx_size_acc, rx_size_cnt; | |
235 | atomic_t do_autopm; | |
236 | u8 rx_size_auto_shrink; | |
237 | ||
238 | struct dentry *debugfs_dentry; | |
b8112469 | 239 | unsigned i6050:1; /* 1 if this is a 6050 based SKU */ |
11a7d0e3 IPG |
240 | }; |
241 | ||
242 | ||
243 | static inline | |
244 | void i2400mu_init(struct i2400mu *i2400mu) | |
245 | { | |
246 | i2400m_init(&i2400mu->i2400m); | |
247 | edc_init(&i2400mu->urb_edc); | |
248 | init_waitqueue_head(&i2400mu->tx_wq); | |
249 | atomic_set(&i2400mu->rx_pending_count, 0); | |
250 | init_waitqueue_head(&i2400mu->rx_wq); | |
251 | i2400mu->rx_size = PAGE_SIZE - sizeof(struct skb_shared_info); | |
252 | atomic_set(&i2400mu->do_autopm, 1); | |
253 | i2400mu->rx_size_auto_shrink = 1; | |
254 | } | |
255 | ||
256 | extern int i2400mu_notification_setup(struct i2400mu *); | |
257 | extern void i2400mu_notification_release(struct i2400mu *); | |
258 | ||
259 | extern int i2400mu_rx_setup(struct i2400mu *); | |
260 | extern void i2400mu_rx_release(struct i2400mu *); | |
261 | extern void i2400mu_rx_kick(struct i2400mu *); | |
262 | ||
263 | extern int i2400mu_tx_setup(struct i2400mu *); | |
264 | extern void i2400mu_tx_release(struct i2400mu *); | |
265 | extern void i2400mu_bus_tx_kick(struct i2400m *); | |
266 | ||
267 | extern ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *, | |
268 | const struct i2400m_bootrom_header *, | |
269 | size_t, int); | |
270 | extern ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *, | |
271 | struct i2400m_bootrom_header *, | |
272 | size_t); | |
273 | #endif /* #ifndef __I2400M_USB_H__ */ |