diff options
author | Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> | 2008-09-17 11:34:21 -0400 |
---|---|---|
committer | David Vrabel <dv02@dv02pc01.europe.root.pri> | 2008-09-17 11:54:28 -0400 |
commit | a21b963aa4a98c645b1fa3799f2e4a6ebb6c974a (patch) | |
tree | bb6c927dea997cd1327c283f2969263475c21674 /drivers/uwb/i1480/i1480u-wlp/netdev.c | |
parent | 1ba47da527121ff704f4e9f27a12c9f32db05022 (diff) |
uwb: add the i1480 WLP driver
Add the driver for the WLP capability of the Intel i1480 device.
Signed-off-by: David Vrabel <david.vrabel@csr.com>
Diffstat (limited to 'drivers/uwb/i1480/i1480u-wlp/netdev.c')
-rw-r--r-- | drivers/uwb/i1480/i1480u-wlp/netdev.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c new file mode 100644 index 000000000000..8802ac43d872 --- /dev/null +++ b/drivers/uwb/i1480/i1480u-wlp/netdev.c | |||
@@ -0,0 +1,368 @@ | |||
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> | ||
44 | #include <linux/uwb/debug.h> | ||
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; | ||
210 | netif_wake_queue(net_dev); | ||
211 | #ifdef i1480u_FLOW_CONTROL | ||
212 | result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);; | ||
213 | if (result < 0) { | ||
214 | dev_err(dev, "Can't submit notification URB: %d\n", result); | ||
215 | goto error_notif_urb_submit; | ||
216 | } | ||
217 | #endif | ||
218 | i1480u->uwb_notifs_handler.cb = i1480u_uwb_notifs_cb; | ||
219 | i1480u->uwb_notifs_handler.data = i1480u; | ||
220 | if (uwb_bg_joined(rc)) | ||
221 | netif_carrier_on(net_dev); | ||
222 | else | ||
223 | netif_carrier_off(net_dev); | ||
224 | uwb_notifs_register(rc, &i1480u->uwb_notifs_handler); | ||
225 | /* Interface is up with an address, now we can create WSS */ | ||
226 | result = wlp_wss_setup(net_dev, &wlp->wss); | ||
227 | if (result < 0) { | ||
228 | dev_err(dev, "Can't create WSS: %d. \n", result); | ||
229 | goto error_notif_deregister; | ||
230 | } | ||
231 | return 0; | ||
232 | error_notif_deregister: | ||
233 | uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler); | ||
234 | #ifdef i1480u_FLOW_CONTROL | ||
235 | error_notif_urb_submit: | ||
236 | #endif | ||
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; | ||
251 | struct uwb_rc *rc = wlp->rc; | ||
252 | |||
253 | BUG_ON(wlp->rc == NULL); | ||
254 | wlp_wss_remove(&wlp->wss); | ||
255 | uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler); | ||
256 | netif_carrier_off(net_dev); | ||
257 | #ifdef i1480u_FLOW_CONTROL | ||
258 | usb_kill_urb(i1480u->notif_urb); | ||
259 | #endif | ||
260 | netif_stop_queue(net_dev); | ||
261 | i1480u_rx_release(i1480u); | ||
262 | i1480u_tx_release(i1480u); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | |||
267 | /** Report statistics */ | ||
268 | struct net_device_stats *i1480u_get_stats(struct net_device *net_dev) | ||
269 | { | ||
270 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
271 | return &i1480u->stats; | ||
272 | } | ||
273 | |||
274 | |||
275 | /** | ||
276 | * | ||
277 | * Change the interface config--we probably don't have to do anything. | ||
278 | */ | ||
279 | int i1480u_set_config(struct net_device *net_dev, struct ifmap *map) | ||
280 | { | ||
281 | int result; | ||
282 | struct i1480u *i1480u = netdev_priv(net_dev); | ||
283 | BUG_ON(i1480u->wlp.rc == NULL); | ||
284 | result = 0; | ||
285 | return result; | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * Change the MTU of the interface | ||
290 | */ | ||
291 | int i1480u_change_mtu(struct net_device *net_dev, int mtu) | ||
292 | { | ||
293 | static union { | ||
294 | struct wlp_tx_hdr tx; | ||
295 | struct wlp_rx_hdr rx; | ||
296 | } i1480u_all_hdrs; | ||
297 | |||
298 | if (mtu < ETH_HLEN) /* We encap eth frames */ | ||
299 | return -ERANGE; | ||
300 | if (mtu > 4000 - sizeof(i1480u_all_hdrs)) | ||
301 | return -ERANGE; | ||
302 | net_dev->mtu = mtu; | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | |||
307 | /** | ||
308 | * Callback function to handle events from UWB | ||
309 | * When we see other devices we know the carrier is ok, | ||
310 | * if we are the only device in the beacon group we set the carrier | ||
311 | * state to off. | ||
312 | * */ | ||
313 | void i1480u_uwb_notifs_cb(void *data, struct uwb_dev *uwb_dev, | ||
314 | enum uwb_notifs event) | ||
315 | { | ||
316 | struct i1480u *i1480u = data; | ||
317 | struct net_device *net_dev = i1480u->net_dev; | ||
318 | struct device *dev = &i1480u->usb_iface->dev; | ||
319 | switch (event) { | ||
320 | case UWB_NOTIF_BG_JOIN: | ||
321 | netif_carrier_on(net_dev); | ||
322 | dev_info(dev, "Link is up\n"); | ||
323 | break; | ||
324 | case UWB_NOTIF_BG_LEAVE: | ||
325 | netif_carrier_off(net_dev); | ||
326 | dev_info(dev, "Link is down\n"); | ||
327 | break; | ||
328 | default: | ||
329 | dev_err(dev, "don't know how to handle event %d from uwb\n", | ||
330 | event); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * Stop the network queue | ||
336 | * | ||
337 | * Enable WLP substack to stop network queue. We also set the flow control | ||
338 | * threshold at this time to prevent the flow control from restarting the | ||
339 | * queue. | ||
340 | * | ||
341 | * we are loosing the current threshold value here ... FIXME? | ||
342 | */ | ||
343 | void i1480u_stop_queue(struct wlp *wlp) | ||
344 | { | ||
345 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | ||
346 | struct net_device *net_dev = i1480u->net_dev; | ||
347 | i1480u->tx_inflight.threshold = 0; | ||
348 | netif_stop_queue(net_dev); | ||
349 | } | ||
350 | |||
351 | /** | ||
352 | * Start the network queue | ||
353 | * | ||
354 | * Enable WLP substack to start network queue. Also re-enable the flow | ||
355 | * control to manage the queue again. | ||
356 | * | ||
357 | * We re-enable the flow control by storing the default threshold in the | ||
358 | * flow control threshold. This means that if the user modified the | ||
359 | * threshold before the queue was stopped and restarted that information | ||
360 | * will be lost. FIXME? | ||
361 | */ | ||
362 | void i1480u_start_queue(struct wlp *wlp) | ||
363 | { | ||
364 | struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); | ||
365 | struct net_device *net_dev = i1480u->net_dev; | ||
366 | i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; | ||
367 | netif_start_queue(net_dev); | ||
368 | } | ||