Commit | Line | Data |
---|---|---|
a21b963a IPG |
1 | /* |
2 | * WUSB Wire Adapter: WLP interface | |
3 | * Driver for the Linux Network stack. | |
4 | * | |
5 | * Copyright (C) 2005-2006 Intel Corporation | |
6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License version | |
10 | * 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
20 | * 02110-1301, USA. | |
21 | * | |
22 | * | |
23 | * FIXME: docs | |
24 | * | |
25 | * Implementation of the netdevice linkage (except tx and rx related stuff). | |
26 | * | |
27 | * ROADMAP: | |
28 | * | |
29 | * ENTRY POINTS (Net device): | |
30 | * | |
31 | * i1480u_open(): Called when we ifconfig up the interface; | |
32 | * associates to a UWB host controller, reserves | |
33 | * bandwidth (MAS), sets up RX USB URB and starts | |
34 | * the queue. | |
35 | * | |
36 | * i1480u_stop(): Called when we ifconfig down a interface; | |
37 | * reverses _open(). | |
38 | * | |
39 | * i1480u_set_config(): | |
40 | */ | |
41 | ||
42 | #include <linux/if_arp.h> | |
43 | #include <linux/etherdevice.h> | |
a01777ec | 44 | |
a21b963a IPG |
45 | #include "i1480u-wlp.h" |
46 | ||
47 | struct i1480u_cmd_set_ip_mas { | |
48 | struct uwb_rccb rccb; | |
49 | struct uwb_dev_addr addr; | |
50 | u8 stream; | |
51 | u8 owner; | |
52 | u8 type; /* enum uwb_drp_type */ | |
53 | u8 baMAS[32]; | |
54 | } __attribute__((packed)); | |
55 | ||
56 | ||
57 | static | |
58 | int i1480u_set_ip_mas( | |
59 | struct uwb_rc *rc, | |
60 | const struct uwb_dev_addr *dstaddr, | |
61 | u8 stream, u8 owner, u8 type, unsigned long *mas) | |
62 | { | |
63 | ||
64 | int result; | |
65 | struct i1480u_cmd_set_ip_mas *cmd; | |
66 | struct uwb_rc_evt_confirm reply; | |
67 | ||
68 | result = -ENOMEM; | |
69 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
70 | if (cmd == NULL) | |
71 | goto error_kzalloc; | |
72 | cmd->rccb.bCommandType = 0xfd; | |
73 | cmd->rccb.wCommand = cpu_to_le16(0x000e); | |
74 | cmd->addr = *dstaddr; | |
75 | cmd->stream = stream; | |
76 | cmd->owner = owner; | |
77 | cmd->type = type; | |
78 | if (mas == NULL) | |
79 | memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS)); | |
80 | else | |
81 | memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS)); | |
82 | reply.rceb.bEventType = 0xfd; | |
83 | reply.rceb.wEvent = cpu_to_le16(0x000e); | |
84 | result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd), | |
85 | &reply.rceb, sizeof(reply)); | |
86 | if (result < 0) | |
87 | goto error_cmd; | |
88 | if (reply.bResultCode != UWB_RC_RES_FAIL) { | |
89 | dev_err(&rc->uwb_dev.dev, | |
90 | "SET-IP-MAS: command execution failed: %d\n", | |
91 | reply.bResultCode); | |
92 | result = -EIO; | |
93 | } | |
94 | error_cmd: | |
95 | kfree(cmd); | |
96 | error_kzalloc: | |
97 | return result; | |
98 | } | |
99 | ||
100 | /* | |
101 | * Inform a WLP interface of a MAS reservation | |
102 | * | |
103 | * @rc is assumed refcnted. | |
104 | */ | |
105 | /* FIXME: detect if remote device is WLP capable? */ | |
106 | static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc, | |
107 | u8 stream, u8 owner, u8 type, unsigned long *mas) | |
108 | { | |
109 | int result = 0; | |
110 | struct device *dev = &rc->uwb_dev.dev; | |
111 | ||
112 | result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner, | |
113 | type, mas); | |
114 | if (result < 0) { | |
115 | char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE]; | |
116 | uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf), | |
117 | &rc->uwb_dev.dev_addr); | |
118 | uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf), | |
119 | &uwb_dev->dev_addr); | |
120 | dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n", | |
121 | rcaddrbuf, devaddrbuf, result); | |
122 | } | |
123 | return result; | |
124 | } | |
125 | ||
126 | /** | |
127 | * Called by bandwidth allocator when change occurs in reservation. | |
128 | * | |
129 | * @rsv: The reservation that is being established, modified, or | |
130 | * terminated. | |
131 | * | |
132 | * When a reservation is established, modified, or terminated the upper layer | |
133 | * (WLP here) needs set/update the currently available Media Access Slots | |
134 | * that can be use for IP traffic. | |
135 | * | |
136 | * Our action taken during failure depends on how the reservation is being | |
137 | * changed: | |
138 | * - if reservation is being established we do nothing if we cannot set the | |
139 | * new MAS to be used | |
140 | * - if reservation is being terminated we revert back to PCA whether the | |
141 | * SET IP MAS command succeeds or not. | |
142 | */ | |
143 | void i1480u_bw_alloc_cb(struct uwb_rsv *rsv) | |
144 | { | |
145 | int result = 0; | |
146 | struct i1480u *i1480u = rsv->pal_priv; | |
147 | struct device *dev = &i1480u->usb_iface->dev; | |
148 | struct uwb_dev *target_dev = rsv->target.dev; | |
149 | struct uwb_rc *rc = i1480u->wlp.rc; | |
150 | u8 stream = rsv->stream; | |
151 | int type = rsv->type; | |
152 | int is_owner = rsv->owner == &rc->uwb_dev; | |
153 | unsigned long *bmp = rsv->mas.bm; | |
154 | ||
155 | dev_err(dev, "WLP callback called - sending set ip mas\n"); | |
156 | /*user cannot change options while setting configuration*/ | |
157 | mutex_lock(&i1480u->options.mutex); | |
158 | switch (rsv->state) { | |
159 | case UWB_RSV_STATE_T_ACCEPTED: | |
160 | case UWB_RSV_STATE_O_ESTABLISHED: | |
161 | result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, | |
162 | type, bmp); | |
163 | if (result < 0) { | |
164 | dev_err(dev, "MAS reservation failed: %d\n", result); | |
165 | goto out; | |
166 | } | |
167 | if (is_owner) { | |
168 | wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, | |
169 | WLP_DRP | stream); | |
170 | wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0); | |
171 | } | |
172 | break; | |
173 | case UWB_RSV_STATE_NONE: | |
174 | /* revert back to PCA */ | |
175 | result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, | |
176 | type, bmp); | |
177 | if (result < 0) | |
178 | dev_err(dev, "MAS reservation failed: %d\n", result); | |
179 | /* Revert to PCA even though SET IP MAS failed. */ | |
180 | wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, | |
181 | i1480u->options.pca_base_priority); | |
182 | wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1); | |
183 | break; | |
184 | default: | |
185 | dev_err(dev, "unexpected WLP reservation state: %s (%d).\n", | |
186 | uwb_rsv_state_str(rsv->state), rsv->state); | |
187 | break; | |
188 | } | |
189 | out: | |
190 | mutex_unlock(&i1480u->options.mutex); | |
191 | return; | |
192 | } | |
193 | ||
194 | /** | |
195 | * | |
196 | * Called on 'ifconfig up' | |
197 | */ | |
198 | int i1480u_open(struct net_device *net_dev) | |
199 | { | |
200 | int result; | |
201 | struct i1480u *i1480u = netdev_priv(net_dev); | |
202 | struct wlp *wlp = &i1480u->wlp; | |
203 | struct uwb_rc *rc; | |
204 | struct device *dev = &i1480u->usb_iface->dev; | |
205 | ||
206 | rc = wlp->rc; | |
207 | result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */ | |
208 | if (result < 0) | |
209 | goto error_rx_setup; | |
e8e1594c DV |
210 | |
211 | result = uwb_radio_start(&wlp->pal); | |
212 | if (result < 0) | |
213 | goto error_radio_start; | |
214 | ||
a21b963a IPG |
215 | netif_wake_queue(net_dev); |
216 | #ifdef i1480u_FLOW_CONTROL | |
a419aef8 | 217 | result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL); |
a21b963a IPG |
218 | if (result < 0) { |
219 | dev_err(dev, "Can't submit notification URB: %d\n", result); | |
220 | goto error_notif_urb_submit; | |
221 | } | |
222 | #endif | |
a21b963a IPG |
223 | /* Interface is up with an address, now we can create WSS */ |
224 | result = wlp_wss_setup(net_dev, &wlp->wss); | |
225 | if (result < 0) { | |
226 | dev_err(dev, "Can't create WSS: %d. \n", result); | |
e8e1594c | 227 | goto error_wss_setup; |
a21b963a IPG |
228 | } |
229 | return 0; | |
e8e1594c | 230 | error_wss_setup: |
a21b963a | 231 | #ifdef i1480u_FLOW_CONTROL |
e8e1594c | 232 | usb_kill_urb(i1480u->notif_urb); |
a21b963a IPG |
233 | error_notif_urb_submit: |
234 | #endif | |
e8e1594c DV |
235 | uwb_radio_stop(&wlp->pal); |
236 | error_radio_start: | |
a21b963a IPG |
237 | netif_stop_queue(net_dev); |
238 | i1480u_rx_release(i1480u); | |
239 | error_rx_setup: | |
240 | return result; | |
241 | } | |
242 | ||
243 | ||
244 | /** | |
245 | * Called on 'ifconfig down' | |
246 | */ | |
247 | int i1480u_stop(struct net_device *net_dev) | |
248 | { | |
249 | struct i1480u *i1480u = netdev_priv(net_dev); | |
250 | struct wlp *wlp = &i1480u->wlp; | |
a21b963a IPG |
251 | |
252 | BUG_ON(wlp->rc == NULL); | |
253 | wlp_wss_remove(&wlp->wss); | |
a21b963a IPG |
254 | netif_carrier_off(net_dev); |
255 | #ifdef i1480u_FLOW_CONTROL | |
256 | usb_kill_urb(i1480u->notif_urb); | |
257 | #endif | |
258 | netif_stop_queue(net_dev); | |
e8e1594c | 259 | uwb_radio_stop(&wlp->pal); |
a21b963a IPG |
260 | i1480u_rx_release(i1480u); |
261 | i1480u_tx_release(i1480u); | |
262 | return 0; | |
263 | } | |
264 | ||
a21b963a IPG |
265 | /** |
266 | * | |
267 | * Change the interface config--we probably don't have to do anything. | |
268 | */ | |
269 | int i1480u_set_config(struct net_device *net_dev, struct ifmap *map) | |
270 | { | |
271 | int result; | |
272 | struct i1480u *i1480u = netdev_priv(net_dev); | |
273 | BUG_ON(i1480u->wlp.rc == NULL); | |
274 | result = 0; | |
275 | return result; | |
276 | } | |
277 | ||
278 | /** | |
279 | * Change the MTU of the interface | |
280 | */ | |
281 | int i1480u_change_mtu(struct net_device *net_dev, int mtu) | |
282 | { | |
283 | static union { | |
284 | struct wlp_tx_hdr tx; | |
285 | struct wlp_rx_hdr rx; | |
286 | } i1480u_all_hdrs; | |
287 | ||
288 | if (mtu < ETH_HLEN) /* We encap eth frames */ | |
289 | return -ERANGE; | |
290 | if (mtu > 4000 - sizeof(i1480u_all_hdrs)) | |
291 | return -ERANGE; | |
292 | net_dev->mtu = mtu; | |
293 | return 0; | |
294 | } | |
295 | ||
a21b963a IPG |
296 | /** |
297 | * Stop the network queue | |
298 | * | |
299 | * Enable WLP substack to stop network queue. We also set the flow control | |
300 | * threshold at this time to prevent the flow control from restarting the | |
301 | * queue. | |
302 | * | |
303 | * we are loosing the current threshold value here ... FIXME? | |
304 | */ | |
305 | void i1480u_stop_queue(struct wlp *wlp) | |
306 | { | |
307 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | |
308 | struct net_device *net_dev = i1480u->net_dev; | |
309 | i1480u->tx_inflight.threshold = 0; | |
310 | netif_stop_queue(net_dev); | |
311 | } | |
312 | ||
313 | /** | |
314 | * Start the network queue | |
315 | * | |
316 | * Enable WLP substack to start network queue. Also re-enable the flow | |
317 | * control to manage the queue again. | |
318 | * | |
319 | * We re-enable the flow control by storing the default threshold in the | |
320 | * flow control threshold. This means that if the user modified the | |
321 | * threshold before the queue was stopped and restarted that information | |
322 | * will be lost. FIXME? | |
323 | */ | |
324 | void i1480u_start_queue(struct wlp *wlp) | |
325 | { | |
326 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | |
327 | struct net_device *net_dev = i1480u->net_dev; | |
328 | i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; | |
329 | netif_start_queue(net_dev); | |
330 | } |