/* * WUSB Wire Adapter: WLP interface * Driver for the Linux Network stack. * * Copyright (C) 2005-2006 Intel Corporation * Inaky Perez-Gonzalez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * FIXME: docs * * Implementation of the netdevice linkage (except tx and rx related stuff). * * ROADMAP: * * ENTRY POINTS (Net device): * * i1480u_open(): Called when we ifconfig up the interface; * associates to a UWB host controller, reserves * bandwidth (MAS), sets up RX USB URB and starts * the queue. * * i1480u_stop(): Called when we ifconfig down a interface; * reverses _open(). * * i1480u_set_config(): */ #include #include #include "i1480u-wlp.h" struct i1480u_cmd_set_ip_mas { struct uwb_rccb rccb; struct uwb_dev_addr addr; u8 stream; u8 owner; u8 type; /* enum uwb_drp_type */ u8 baMAS[32]; } __attribute__((packed)); static int i1480u_set_ip_mas( struct uwb_rc *rc, const struct uwb_dev_addr *dstaddr, u8 stream, u8 owner, u8 type, unsigned long *mas) { int result; struct i1480u_cmd_set_ip_mas *cmd; struct uwb_rc_evt_confirm reply; result = -ENOMEM; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) goto error_kzalloc; cmd->rccb.bCommandType = 0xfd; cmd->rccb.wCommand = cpu_to_le16(0x000e); cmd->addr = *dstaddr; cmd->stream = stream; cmd->owner = owner; cmd->type = type; if (mas == NULL) memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS)); else memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS)); reply.rceb.bEventType = 0xfd; reply.rceb.wEvent = cpu_to_le16(0x000e); result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd), &reply.rceb, sizeof(reply)); if (result < 0) goto error_cmd; if (reply.bResultCode != UWB_RC_RES_FAIL) { dev_err(&rc->uwb_dev.dev, "SET-IP-MAS: command execution failed: %d\n", reply.bResultCode); result = -EIO; } error_cmd: kfree(cmd); error_kzalloc: return result; } /* * Inform a WLP interface of a MAS reservation * * @rc is assumed refcnted. */ /* FIXME: detect if remote device is WLP capable? */ static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc, u8 stream, u8 owner, u8 type, unsigned long *mas) { int result = 0; struct device *dev = &rc->uwb_dev.dev; result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner, type, mas); if (result < 0) { char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE]; uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf), &rc->uwb_dev.dev_addr); uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf), &uwb_dev->dev_addr); dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n", rcaddrbuf, devaddrbuf, result); } return result; } /** * Called by bandwidth allocator when change occurs in reservation. * * @rsv: The reservation that is being established, modified, or * terminated. * * When a reservation is established, modified, or terminated the upper layer * (WLP here) needs set/update the currently available Media Access Slots * that can be use for IP traffic. * * Our action taken during failure depends on how the reservation is being * changed: * - if reservation is being established we do nothing if we cannot set the * new MAS to be used * - if reservation is being terminated we revert back to PCA whether the * SET IP MAS command succeeds or not. */ void i1480u_bw_alloc_cb(struct uwb_rsv *rsv) { int result = 0; struct i1480u *i1480u = rsv->pal_priv; struct device *dev = &i1480u->usb_iface->dev; struct uwb_dev *target_dev = rsv->target.dev; struct uwb_rc *rc = i1480u->wlp.rc; u8 stream = rsv->stream; int type = rsv->type; int is_owner = rsv->owner == &rc->uwb_dev; unsigned long *bmp = rsv->mas.bm; dev_err(dev, "WLP callback called - sending set ip mas\n"); /*user cannot change options while setting configuration*/ mutex_lock(&i1480u->options.mutex); switch (rsv->state) { case UWB_RSV_STATE_T_ACCEPTED: case UWB_RSV_STATE_O_ESTABLISHED: result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, type, bmp); if (result < 0) { dev_err(dev, "MAS reservation failed: %d\n", result); goto out; } if (is_owner) { wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, WLP_DRP | stream); wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 0); } break; case UWB_RSV_STATE_NONE: /* revert back to PCA */ result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner, type, bmp); if (result < 0) dev_err(dev, "MAS reservation failed: %d\n", result); /* Revert to PCA even though SET IP MAS failed. */ wlp_tx_hdr_set_delivery_id_type(&i1480u->options.def_tx_hdr, i1480u->options.pca_base_priority); wlp_tx_hdr_set_rts_cts(&i1480u->options.def_tx_hdr, 1); break; default: dev_err(dev, "unexpected WLP reservation state: %s (%d).\n", uwb_rsv_state_str(rsv->state), rsv->state); break; } out: mutex_unlock(&i1480u->options.mutex); return; } /** * * Called on 'ifconfig up' */ int i1480u_open(struct net_device *net_dev) { int result; struct i1480u *i1480u = netdev_priv(net_dev); struct wlp *wlp = &i1480u->wlp; struct uwb_rc *rc; struct device *dev = &i1480u->usb_iface->dev; rc = wlp->rc; result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */ if (result < 0) goto error_rx_setup; result = uwb_radio_start(&wlp->pal); if (result < 0) goto error_radio_start; netif_wake_queue(net_dev); #ifdef i1480u_FLOW_CONTROL result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL); if (result < 0) { dev_err(dev, "Can't submit notification URB: %d\n", result); goto error_notif_urb_submit; } #endif /* Interface is up with an address, now we can create WSS */ result = wlp_wss_setup(net_dev, &wlp->wss); if (result < 0) { dev_err(dev, "Can't create WSS: %d. \n", result); goto error_wss_setup; } return 0; error_wss_setup: #ifdef i1480u_FLOW_CONTROL usb_kill_urb(i1480u->notif_urb); error_notif_urb_submit: #endif uwb_radio_stop(&wlp->pal); error_radio_start: netif_stop_queue(net_dev); i1480u_rx_release(i1480u); error_rx_setup: return result; } /** * Called on 'ifconfig down' */ int i1480u_stop(struct net_device *net_dev) { struct i1480u *i1480u = netdev_priv(net_dev); struct wlp *wlp = &i1480u->wlp; BUG_ON(wlp->rc == NULL); wlp_wss_remove(&wlp->wss); netif_carrier_off(net_dev); #ifdef i1480u_FLOW_CONTROL usb_kill_urb(i1480u->notif_urb); #endif netif_stop_queue(net_dev); uwb_radio_stop(&wlp->pal); i1480u_rx_release(i1480u); i1480u_tx_release(i1480u); return 0; } /** * * Change the interface config--we probably don't have to do anything. */ int i1480u_set_config(struct net_device *net_dev, struct ifmap *map) { int result; struct i1480u *i1480u = netdev_priv(net_dev); BUG_ON(i1480u->wlp.rc == NULL); result = 0; return result; } /** * Change the MTU of the interface */ int i1480u_change_mtu(struct net_device *net_dev, int mtu) { static union { struct wlp_tx_hdr tx; struct wlp_rx_hdr rx; } i1480u_all_hdrs; if (mtu < ETH_HLEN) /* We encap eth frames */ return -ERANGE; if (mtu > 4000 - sizeof(i1480u_all_hdrs)) return -ERANGE; net_dev->mtu = mtu; return 0; } /** * Stop the network queue * * Enable WLP substack to stop network queue. We also set the flow control * threshold at this time to prevent the flow control from restarting the * queue. * * we are loosing the current threshold value here ... FIXME? */ void i1480u_stop_queue(struct wlp *wlp) { struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); struct net_device *net_dev = i1480u->net_dev; i1480u->tx_inflight.threshold = 0; netif_stop_queue(net_dev); } /** * Start the network queue * * Enable WLP substack to start network queue. Also re-enable the flow * control to manage the queue again. * * We re-enable the flow control by storing the default threshold in the * flow control threshold. This means that if the user modified the * threshold before the queue was stopped and restarted that information * will be lost. FIXME? */ void i1480u_start_queue(struct wlp *wlp) { struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp); struct net_device *net_dev = i1480u->net_dev; i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD; netif_start_queue(net_dev); }