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