Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 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> |
David Vrabel | a01777e | 2008-12-22 18:30:29 +0000 | [diff] [blame] | 44 | |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 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; |
David Vrabel | e8e1594 | 2008-11-17 16:16:51 +0000 | [diff] [blame] | 210 | |
| 211 | result = uwb_radio_start(&wlp->pal); |
| 212 | if (result < 0) |
| 213 | goto error_radio_start; |
| 214 | |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 215 | netif_wake_queue(net_dev); |
| 216 | #ifdef i1480u_FLOW_CONTROL |
| 217 | result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);; |
| 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 |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 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); |
David Vrabel | e8e1594 | 2008-11-17 16:16:51 +0000 | [diff] [blame] | 227 | goto error_wss_setup; |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 228 | } |
| 229 | return 0; |
David Vrabel | e8e1594 | 2008-11-17 16:16:51 +0000 | [diff] [blame] | 230 | error_wss_setup: |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 231 | #ifdef i1480u_FLOW_CONTROL |
David Vrabel | e8e1594 | 2008-11-17 16:16:51 +0000 | [diff] [blame] | 232 | usb_kill_urb(i1480u->notif_urb); |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 233 | error_notif_urb_submit: |
| 234 | #endif |
David Vrabel | e8e1594 | 2008-11-17 16:16:51 +0000 | [diff] [blame] | 235 | uwb_radio_stop(&wlp->pal); |
| 236 | error_radio_start: |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 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; |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 251 | |
| 252 | BUG_ON(wlp->rc == NULL); |
| 253 | wlp_wss_remove(&wlp->wss); |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 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); |
David Vrabel | e8e1594 | 2008-11-17 16:16:51 +0000 | [diff] [blame] | 259 | uwb_radio_stop(&wlp->pal); |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 260 | i1480u_rx_release(i1480u); |
| 261 | i1480u_tx_release(i1480u); |
| 262 | return 0; |
| 263 | } |
| 264 | |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 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 | |
Inaky Perez-Gonzalez | a21b963 | 2008-09-17 16:34:21 +0100 | [diff] [blame] | 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 | } |