#include #include #include #include #include #include #include #include #include #include #include #include struct pvcan_private { struct can_priv can; /* must be the first member! */ struct xenbus_device *xendev; struct net_device *candev; unsigned int evtchn_rx; unsigned int evtchn_tx; struct xen_pvcan_rx_back_ring rx_ring; struct xen_pvcan_tx_back_ring tx_ring; int irq_rx; int irq_tx; grant_ref_t tx_ring_ref; grant_ref_t rx_ring_ref; }; irqreturn_t pvcan_interrupt_rx(int irq, void *data) { struct net_device *candev = (struct net_device *) data; struct pvcan_private *priv = netdev_priv(candev); struct net_device_stats *stats = &candev->stats; RING_IDX rcons, rprod; struct sk_buff *skb; struct can_frame *new_cfd; struct pvcan_request req; struct pvcan_response *res; int more_to_do; get_more_requests: rcons = priv->rx_ring.req_cons; rprod = priv->rx_ring.sring->req_prod; rmb(); if (RING_REQUEST_PROD_OVERFLOW(&priv->rx_ring, rprod)) { dev_err(&candev->dev, "ring overflow\n"); return IRQ_HANDLED; } while (rcons != rprod) { req = *RING_GET_REQUEST(&priv->rx_ring, rcons); stats->rx_packets++; stats->rx_bytes += req.cfd.len; skb = alloc_can_skb(candev, &new_cfd); skb->pkt_type = PACKET_BROADCAST; skb->dev = candev; skb->ip_summed = CHECKSUM_UNNECESSARY; memcpy(skb->data, &req.cfd, sizeof(req.cfd)); res = RING_GET_RESPONSE(&priv->rx_ring, priv->rx_ring.rsp_prod_pvt); barrier(); priv->rx_ring.rsp_prod_pvt++; priv->rx_ring.req_cons = ++rcons; netif_rx(skb); } RING_FINAL_CHECK_FOR_REQUESTS(&priv->rx_ring, more_to_do); if (!!more_to_do) { goto get_more_requests; } // Notify the frontend-driver, that more // frames can be sent notify_remote_via_irq(priv->irq_tx); return IRQ_HANDLED; } /* Dummy interrupt handler that is never used */ irqreturn_t pvcan_interrupt_tx(int irq, void *data) { return IRQ_HANDLED; } /* Transmitt CAN frames to domU */ static netdev_tx_t pvcan_tx(struct sk_buff *skb, struct net_device *dev) { struct pvcan_private *priv = netdev_priv(dev); struct pvcan_response *rsp; struct can_frame *cfd = (struct can_frame *) skb->data; struct net_device_stats *stats = &dev->stats; bool notify; if (can_dropped_invalid_skb(dev, skb)) { stats->tx_dropped++; return NETDEV_TX_OK; } stats->tx_packets++; stats->tx_bytes += cfd->can_dlc; rsp = RING_GET_RESPONSE(&priv->tx_ring, priv->tx_ring.rsp_prod_pvt); barrier(); memcpy(&rsp->cfd, cfd, sizeof(*cfd)); wmb(); priv->tx_ring.rsp_prod_pvt++; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&priv->tx_ring, notify); if (notify) notify_remote_via_irq(priv->irq_rx); consume_skb(skb); return NETDEV_TX_OK; } static int pvcan_change_mtu(struct net_device *dev, int new_mtu) { /* Do not allow changing the MTU while running */ if (dev->flags & IFF_UP) return -EBUSY; if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU) return -EINVAL; dev->mtu = new_mtu; return 0; } static int pvcan_open(struct net_device *dev) { if (!netif_carrier_ok(dev)) netif_carrier_on(dev); netif_start_queue(dev); return 0; } static int pvcan_stop(struct net_device *dev) { netif_stop_queue(dev); return 0; } static const struct net_device_ops pvcan_netdev_ops = { .ndo_open = pvcan_open, .ndo_stop = pvcan_stop, .ndo_start_xmit = pvcan_tx, .ndo_change_mtu = pvcan_change_mtu, }; static void backend_connect(struct xenbus_device *dev) { struct net_device *candev; struct pvcan_private *priv; struct xen_pvcan_rx_sring *pvcan_rx_sring; struct xen_pvcan_tx_sring *pvcan_tx_sring; int rc; void *addr; candev = alloc_candev(sizeof(struct pvcan_private), 1); if (!candev) { xenbus_dev_fatal(dev, -ENOMEM, "allocating private structure"); rc = -ENOMEM; goto err_out; } priv = netdev_priv(candev); dev_set_drvdata(&dev->dev, priv); priv->xendev = dev; priv->candev = candev; priv->tx_ring_ref = xenbus_read_unsigned(dev->otherend, "tx-ring-ref", 1); if (priv->tx_ring_ref <= 0) { dev_err(&dev->dev, "Could not read tx-ring-ref\n"); goto free_candev; } dev_dbg(&dev->dev, "tx-ring-ref = %i\n", priv->tx_ring_ref); priv->rx_ring_ref = xenbus_read_unsigned(dev->otherend, "rx-ring-ref", 1); if (priv->rx_ring_ref <= 0) { dev_err(&dev->dev, "Could not read rx-ring-ref\n"); goto free_candev; } dev_dbg(&dev->dev, "rx-ring-ref = %i\n", priv->rx_ring_ref); priv->evtchn_rx = xenbus_read_unsigned(dev->otherend, "event-channel-rx", 1); if (priv->evtchn_rx <= 0) { dev_err(&dev->dev, "Could not read evtchn\n"); goto free_candev; } dev_dbg(&dev->dev, "event-channel-rx = %i\n", priv->evtchn_rx); priv->evtchn_tx = xenbus_read_unsigned(dev->otherend, "event-channel-tx", 1); if (priv->evtchn_tx <= 0) { dev_err(&dev->dev, "Could not read evtchn_tx\n"); goto free_candev; } dev_dbg(&dev->dev, "event-channel-tx = %i\n", priv->evtchn_tx); rc = xenbus_map_ring_valloc(dev, &priv->rx_ring_ref, 1, &addr); if (rc) { dev_err(&dev->dev, "Could not map rx-ring\n"); goto free_ring; } pvcan_rx_sring = addr; BACK_RING_INIT(&priv->rx_ring, pvcan_rx_sring, PAGE_SIZE); rc = xenbus_map_ring_valloc(dev, &priv->tx_ring_ref, 1, &addr); if (rc) { dev_err(&dev->dev, "Could not map tx-ring\n"); goto free_candev; } pvcan_tx_sring = addr; BACK_RING_INIT(&priv->tx_ring, pvcan_tx_sring, PAGE_SIZE); priv->irq_rx = bind_interdomain_evtchn_to_irqhandler(dev->otherend_id, priv->evtchn_rx, pvcan_interrupt_rx, 0, "pvcan-backend-rx", candev); if (priv->irq_rx < 0) { dev_err(&dev->dev, "Could not allocate irq\n"); goto free_ring; } dev_dbg(&candev->dev, "setting up can interface, with irq = %i\n", priv->irq_rx); priv->irq_tx = bind_interdomain_evtchn_to_irqhandler(dev->otherend_id, priv->evtchn_tx, pvcan_interrupt_tx, 0, "pvcan-backend-tx", candev); if (priv->irq_tx < 0) { dev_err(&dev->dev, "Could not allocate irq\n"); goto free_ring; } dev_dbg(&candev->dev, "setting up can interface, with irq = %i\n", priv->irq_tx); /* Create pvcan interface */ candev->type = ARPHRD_CAN; candev->mtu = CAN_MTU; candev->hard_header_len = 0; candev->addr_len = 0; candev->tx_queue_len = 16; candev->flags = IFF_NOARP; candev->netdev_ops = &pvcan_netdev_ops; candev->needs_free_netdev = true; dev_dbg(&candev->dev, "register can interface\n"); rc = register_candev(candev); if (rc == 0) return; dev_err(&candev->dev, "Could not register pvcan interface\n"); if (priv->irq_tx) { unbind_from_irqhandler(priv->irq_tx, candev); priv->irq_tx = 0; } if (priv->irq_rx) { unbind_from_irqhandler(priv->irq_rx, candev); priv->irq_rx = 0; } free_ring: xenbus_unmap_ring_vfree(dev, pvcan_rx_sring); xenbus_unmap_ring_vfree(dev, pvcan_tx_sring); free_candev: free_candev(candev); err_out: return; } static void backend_disconnect(struct xenbus_device *dev) { struct pvcan_private *priv = dev_get_drvdata(&dev->dev); struct net_device *candev = priv->candev; /* Disable irq */ if (priv->irq_tx) { unbind_from_irqhandler(priv->irq_tx, candev); priv->irq_tx = 0; } if (priv->irq_rx) { unbind_from_irqhandler(priv->irq_rx, candev); priv->irq_rx = 0; } /* Un allocate rings */ if (priv->rx_ring.sring) { xenbus_unmap_ring_vfree(dev, priv->rx_ring.sring); priv->rx_ring.sring = NULL; } if (priv->tx_ring.sring) { xenbus_unmap_ring_vfree(dev, priv->tx_ring.sring); priv->tx_ring.sring = NULL; } if (priv->candev) { unregister_netdev(candev); priv->candev = NULL; } } static void set_backend_state(struct xenbus_device *dev, enum xenbus_state state) { while (dev->state != state) { switch (dev->state) { case XenbusStateInitialised: case XenbusStateInitialising: switch (state) { case XenbusStateInitWait: case XenbusStateConnected: case XenbusStateClosing: xenbus_switch_state(dev, XenbusStateInitWait); break; case XenbusStateClosed: xenbus_switch_state(dev, XenbusStateClosed); break; default: BUG(); } break; case XenbusStateClosed: { switch (state) { case XenbusStateInitWait: case XenbusStateConnected: xenbus_switch_state(dev, XenbusStateInitWait); break; case XenbusStateClosing: xenbus_switch_state(dev, XenbusStateClosing); break; default: BUG(); } break; } case XenbusStateInitWait: { switch (state) { case XenbusStateConnected: backend_connect(dev); xenbus_switch_state(dev, XenbusStateConnected); break; case XenbusStateClosing: case XenbusStateClosed: xenbus_switch_state(dev, XenbusStateClosing); break; default: BUG(); } break; } case XenbusStateConnected: { switch (state) { case XenbusStateInitWait: case XenbusStateClosing: case XenbusStateClosed: backend_disconnect(dev); xenbus_switch_state(dev, XenbusStateClosing); break; default: BUG(); } break; } case XenbusStateClosing: { switch (state) { case XenbusStateInitWait: case XenbusStateConnected: case XenbusStateClosed: xenbus_switch_state(dev, XenbusStateClosed); break; default: BUG(); } break; } default: BUG(); } } } static int pvcanback_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { xenbus_switch_state(dev, XenbusStateInitialised); return 0; } static int pvcanback_remove(struct xenbus_device *dev) { backend_disconnect(dev); xenbus_switch_state(dev, XenbusStateClosed); return 0; } static void pvcanback_otherend_changed(struct xenbus_device *dev, enum xenbus_state frontend_state) { switch (frontend_state) { case XenbusStateInitialising: set_backend_state(dev, XenbusStateInitWait); break; case XenbusStateInitialised: break; case XenbusStateConnected: set_backend_state(dev, XenbusStateConnected); break; case XenbusStateClosing: set_backend_state(dev, XenbusStateClosing); break; case XenbusStateClosed: case XenbusStateUnknown: set_backend_state(dev, XenbusStateClosed); break; default: xenbus_dev_fatal(dev, -EINVAL, "saw state %s (%d) at frontend", xenbus_strstate(frontend_state), frontend_state); break; } } static const struct xenbus_device_id pvcanback_ids[] = { { "pvcan" }, { "" } }; static struct xenbus_driver pvcanback_driver = { .ids = pvcanback_ids, .probe = pvcanback_probe, .remove = pvcanback_remove, .otherend_changed = pvcanback_otherend_changed, }; static int __init pvcanback_init(void) { printk(KERN_NOTICE "XEN PV CAN backend driver init\n"); return xenbus_register_backend(&pvcanback_driver); } module_init(pvcanback_init); static void __exit pvcanback_exit(void) { xenbus_unregister_driver(&pvcanback_driver); } module_exit(pvcanback_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS("xen:pvcan-back");