X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fnet%2Fsunvnet.c;h=6415ce15c2efef51721b0a50b181ee296f98ad28;hb=ea3a13b7a19f0d7d7344494047c9aa8bb32b6678;hp=b801e3b3a11a002907d602560e3abfec4cd5234a;hpb=485cf925d8b7a6b3c62fe5f1e167f2d0d4edf32a;p=linux-2.6-omap-h63xx.git diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index b801e3b3a11..6415ce15c2e 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -459,6 +459,22 @@ static int vnet_nack(struct vnet_port *port, void *msgbuf) return 0; } +static int handle_mcast(struct vnet_port *port, void *msgbuf) +{ + struct vio_net_mcast_info *pkt = msgbuf; + + if (pkt->tag.stype != VIO_SUBTYPE_ACK) + printk(KERN_ERR PFX "%s: Got unexpected MCAST reply " + "[%02x:%02x:%04x:%08x]\n", + port->vp->dev->name, + pkt->tag.type, + pkt->tag.stype, + pkt->tag.stype_env, + pkt->tag.sid); + + return 0; +} + static void maybe_tx_wakeup(struct vnet *vp) { struct net_device *dev = vp->dev; @@ -544,7 +560,10 @@ static void vnet_event(void *arg, int event) err = vnet_nack(port, &msgbuf); } } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { - err = vio_control_pkt_engine(vio, &msgbuf); + if (msgbuf.tag.stype_env == VNET_MCAST_INFO) + err = handle_mcast(port, &msgbuf); + else + err = vio_control_pkt_engine(vio, &msgbuf); if (err) break; } else { @@ -731,9 +750,122 @@ static int vnet_close(struct net_device *dev) return 0; } +static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr) +{ + struct vnet_mcast_entry *m; + + for (m = vp->mcast_list; m; m = m->next) { + if (!memcmp(m->addr, addr, ETH_ALEN)) + return m; + } + return NULL; +} + +static void __update_mc_list(struct vnet *vp, struct net_device *dev) +{ + struct dev_addr_list *p; + + for (p = dev->mc_list; p; p = p->next) { + struct vnet_mcast_entry *m; + + m = __vnet_mc_find(vp, p->dmi_addr); + if (m) { + m->hit = 1; + continue; + } + + if (!m) { + m = kzalloc(sizeof(*m), GFP_ATOMIC); + if (!m) + continue; + memcpy(m->addr, p->dmi_addr, ETH_ALEN); + m->hit = 1; + + m->next = vp->mcast_list; + vp->mcast_list = m; + } + } +} + +static void __send_mc_list(struct vnet *vp, struct vnet_port *port) +{ + struct vio_net_mcast_info info; + struct vnet_mcast_entry *m, **pp; + int n_addrs; + + memset(&info, 0, sizeof(info)); + + info.tag.type = VIO_TYPE_CTRL; + info.tag.stype = VIO_SUBTYPE_INFO; + info.tag.stype_env = VNET_MCAST_INFO; + info.tag.sid = vio_send_sid(&port->vio); + info.set = 1; + + n_addrs = 0; + for (m = vp->mcast_list; m; m = m->next) { + if (m->sent) + continue; + m->sent = 1; + memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], + m->addr, ETH_ALEN); + if (++n_addrs == VNET_NUM_MCAST) { + info.count = n_addrs; + + (void) vio_ldc_send(&port->vio, &info, + sizeof(info)); + n_addrs = 0; + } + } + if (n_addrs) { + info.count = n_addrs; + (void) vio_ldc_send(&port->vio, &info, sizeof(info)); + } + + info.set = 0; + + n_addrs = 0; + pp = &vp->mcast_list; + while ((m = *pp) != NULL) { + if (m->hit) { + m->hit = 0; + pp = &m->next; + continue; + } + + memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], + m->addr, ETH_ALEN); + if (++n_addrs == VNET_NUM_MCAST) { + info.count = n_addrs; + (void) vio_ldc_send(&port->vio, &info, + sizeof(info)); + n_addrs = 0; + } + + *pp = m->next; + kfree(m); + } + if (n_addrs) { + info.count = n_addrs; + (void) vio_ldc_send(&port->vio, &info, sizeof(info)); + } +} + static void vnet_set_rx_mode(struct net_device *dev) { - /* XXX Implement multicast support XXX */ + struct vnet *vp = netdev_priv(dev); + struct vnet_port *port; + unsigned long flags; + + spin_lock_irqsave(&vp->lock, flags); + if (!list_empty(&vp->port_list)) { + port = list_entry(vp->port_list.next, struct vnet_port, list); + + if (port->switch_port) { + __update_mc_list(vp, dev); + __send_mc_list(vp, port); + } + } + spin_unlock_irqrestore(&vp->lock, flags); } static int vnet_change_mtu(struct net_device *dev, int new_mtu) @@ -774,7 +906,6 @@ static const struct ethtool_ops vnet_ethtool_ops = { .get_msglevel = vnet_get_msglevel, .set_msglevel = vnet_set_msglevel, .get_link = ethtool_op_get_link, - .get_perm_addr = ethtool_op_get_perm_addr, }; static void vnet_port_free_tx_bufs(struct vnet_port *port) @@ -999,7 +1130,7 @@ static struct vio_driver_ops vnet_vio_ops = { .handshake_complete = vnet_handshake_complete, }; -static void print_version(void) +static void __devinit print_version(void) { static int version_printed; @@ -1018,6 +1149,7 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev, struct vnet *vp; const u64 *rmac; int len, i, err, switch_port; + DECLARE_MAC_BUF(mac); print_version(); @@ -1070,6 +1202,7 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev, switch_port = 0; if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) switch_port = 1; + port->switch_port = switch_port; spin_lock_irqsave(&vp->lock, flags); if (switch_port) @@ -1081,12 +1214,9 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev, dev_set_drvdata(&vdev->dev, port); - printk(KERN_INFO "%s: PORT ( remote-mac ", vp->dev->name); - for (i = 0; i < 6; i++) - printk("%2.2x%c", port->raddr[i], i == 5 ? ' ' : ':'); - if (switch_port) - printk("switch-port "); - printk(")\n"); + printk(KERN_INFO "%s: PORT ( remote-mac %s%s )\n", + vp->dev->name, print_mac(mac, port->raddr), + switch_port ? " switch-port" : ""); vio_port_up(&port->vio); @@ -1136,7 +1266,7 @@ static struct vio_device_id vnet_port_match[] = { }, {}, }; -MODULE_DEVICE_TABLE(vio, vnet_match); +MODULE_DEVICE_TABLE(vio, vnet_port_match); static struct vio_driver vnet_port_driver = { .id_table = vnet_port_match,