diff options
Diffstat (limited to 'drivers/net/sunvnet.c')
| -rw-r--r-- | drivers/net/sunvnet.c | 260 |
1 files changed, 129 insertions, 131 deletions
diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index 8a667c13faef..b801e3b3a11a 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <linux/netdevice.h> | 12 | #include <linux/netdevice.h> |
| 13 | #include <linux/ethtool.h> | 13 | #include <linux/ethtool.h> |
| 14 | #include <linux/etherdevice.h> | 14 | #include <linux/etherdevice.h> |
| 15 | #include <linux/mutex.h> | ||
| 15 | 16 | ||
| 16 | #include <asm/vio.h> | 17 | #include <asm/vio.h> |
| 17 | #include <asm/ldc.h> | 18 | #include <asm/ldc.h> |
| @@ -497,6 +498,8 @@ static void vnet_event(void *arg, int event) | |||
| 497 | vio_link_state_change(vio, event); | 498 | vio_link_state_change(vio, event); |
| 498 | spin_unlock_irqrestore(&vio->lock, flags); | 499 | spin_unlock_irqrestore(&vio->lock, flags); |
| 499 | 500 | ||
| 501 | if (event == LDC_EVENT_RESET) | ||
| 502 | vio_port_up(vio); | ||
| 500 | return; | 503 | return; |
| 501 | } | 504 | } |
| 502 | 505 | ||
| @@ -875,6 +878,115 @@ err_out: | |||
| 875 | return err; | 878 | return err; |
| 876 | } | 879 | } |
| 877 | 880 | ||
| 881 | static LIST_HEAD(vnet_list); | ||
| 882 | static DEFINE_MUTEX(vnet_list_mutex); | ||
| 883 | |||
| 884 | static struct vnet * __devinit vnet_new(const u64 *local_mac) | ||
| 885 | { | ||
| 886 | struct net_device *dev; | ||
| 887 | struct vnet *vp; | ||
| 888 | int err, i; | ||
| 889 | |||
| 890 | dev = alloc_etherdev(sizeof(*vp)); | ||
| 891 | if (!dev) { | ||
| 892 | printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); | ||
| 893 | return ERR_PTR(-ENOMEM); | ||
| 894 | } | ||
| 895 | |||
| 896 | for (i = 0; i < ETH_ALEN; i++) | ||
| 897 | dev->dev_addr[i] = (*local_mac >> (5 - i) * 8) & 0xff; | ||
| 898 | |||
| 899 | memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); | ||
| 900 | |||
| 901 | vp = netdev_priv(dev); | ||
| 902 | |||
| 903 | spin_lock_init(&vp->lock); | ||
| 904 | vp->dev = dev; | ||
| 905 | |||
| 906 | INIT_LIST_HEAD(&vp->port_list); | ||
| 907 | for (i = 0; i < VNET_PORT_HASH_SIZE; i++) | ||
| 908 | INIT_HLIST_HEAD(&vp->port_hash[i]); | ||
| 909 | INIT_LIST_HEAD(&vp->list); | ||
| 910 | vp->local_mac = *local_mac; | ||
| 911 | |||
| 912 | dev->open = vnet_open; | ||
| 913 | dev->stop = vnet_close; | ||
| 914 | dev->set_multicast_list = vnet_set_rx_mode; | ||
| 915 | dev->set_mac_address = vnet_set_mac_addr; | ||
| 916 | dev->tx_timeout = vnet_tx_timeout; | ||
| 917 | dev->ethtool_ops = &vnet_ethtool_ops; | ||
| 918 | dev->watchdog_timeo = VNET_TX_TIMEOUT; | ||
| 919 | dev->change_mtu = vnet_change_mtu; | ||
| 920 | dev->hard_start_xmit = vnet_start_xmit; | ||
| 921 | |||
| 922 | err = register_netdev(dev); | ||
| 923 | if (err) { | ||
| 924 | printk(KERN_ERR PFX "Cannot register net device, " | ||
| 925 | "aborting.\n"); | ||
| 926 | goto err_out_free_dev; | ||
| 927 | } | ||
| 928 | |||
| 929 | printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); | ||
| 930 | |||
| 931 | for (i = 0; i < 6; i++) | ||
| 932 | printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); | ||
| 933 | |||
| 934 | list_add(&vp->list, &vnet_list); | ||
| 935 | |||
| 936 | return vp; | ||
| 937 | |||
| 938 | err_out_free_dev: | ||
| 939 | free_netdev(dev); | ||
| 940 | |||
| 941 | return ERR_PTR(err); | ||
| 942 | } | ||
| 943 | |||
| 944 | static struct vnet * __devinit vnet_find_or_create(const u64 *local_mac) | ||
| 945 | { | ||
| 946 | struct vnet *iter, *vp; | ||
| 947 | |||
| 948 | mutex_lock(&vnet_list_mutex); | ||
| 949 | vp = NULL; | ||
| 950 | list_for_each_entry(iter, &vnet_list, list) { | ||
| 951 | if (iter->local_mac == *local_mac) { | ||
| 952 | vp = iter; | ||
| 953 | break; | ||
| 954 | } | ||
| 955 | } | ||
| 956 | if (!vp) | ||
| 957 | vp = vnet_new(local_mac); | ||
| 958 | mutex_unlock(&vnet_list_mutex); | ||
| 959 | |||
| 960 | return vp; | ||
| 961 | } | ||
| 962 | |||
| 963 | static const char *local_mac_prop = "local-mac-address"; | ||
| 964 | |||
| 965 | static struct vnet * __devinit vnet_find_parent(struct mdesc_handle *hp, | ||
| 966 | u64 port_node) | ||
| 967 | { | ||
| 968 | const u64 *local_mac = NULL; | ||
| 969 | u64 a; | ||
| 970 | |||
| 971 | mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { | ||
| 972 | u64 target = mdesc_arc_target(hp, a); | ||
| 973 | const char *name; | ||
| 974 | |||
| 975 | name = mdesc_get_property(hp, target, "name", NULL); | ||
| 976 | if (!name || strcmp(name, "network")) | ||
| 977 | continue; | ||
| 978 | |||
| 979 | local_mac = mdesc_get_property(hp, target, | ||
| 980 | local_mac_prop, NULL); | ||
| 981 | if (local_mac) | ||
| 982 | break; | ||
| 983 | } | ||
| 984 | if (!local_mac) | ||
| 985 | return ERR_PTR(-ENODEV); | ||
| 986 | |||
| 987 | return vnet_find_or_create(local_mac); | ||
| 988 | } | ||
| 989 | |||
| 878 | static struct ldc_channel_config vnet_ldc_cfg = { | 990 | static struct ldc_channel_config vnet_ldc_cfg = { |
| 879 | .event = vnet_event, | 991 | .event = vnet_event, |
| 880 | .mtu = 64, | 992 | .mtu = 64, |
| @@ -887,6 +999,14 @@ static struct vio_driver_ops vnet_vio_ops = { | |||
| 887 | .handshake_complete = vnet_handshake_complete, | 999 | .handshake_complete = vnet_handshake_complete, |
| 888 | }; | 1000 | }; |
| 889 | 1001 | ||
| 1002 | static void print_version(void) | ||
| 1003 | { | ||
| 1004 | static int version_printed; | ||
| 1005 | |||
| 1006 | if (version_printed++ == 0) | ||
| 1007 | printk(KERN_INFO "%s", version); | ||
| 1008 | } | ||
| 1009 | |||
| 890 | const char *remote_macaddr_prop = "remote-mac-address"; | 1010 | const char *remote_macaddr_prop = "remote-mac-address"; |
| 891 | 1011 | ||
| 892 | static int __devinit vnet_port_probe(struct vio_dev *vdev, | 1012 | static int __devinit vnet_port_probe(struct vio_dev *vdev, |
| @@ -899,14 +1019,17 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev, | |||
| 899 | const u64 *rmac; | 1019 | const u64 *rmac; |
| 900 | int len, i, err, switch_port; | 1020 | int len, i, err, switch_port; |
| 901 | 1021 | ||
| 902 | vp = dev_get_drvdata(vdev->dev.parent); | 1022 | print_version(); |
| 903 | if (!vp) { | ||
| 904 | printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); | ||
| 905 | return -ENODEV; | ||
| 906 | } | ||
| 907 | 1023 | ||
| 908 | hp = mdesc_grab(); | 1024 | hp = mdesc_grab(); |
| 909 | 1025 | ||
| 1026 | vp = vnet_find_parent(hp, vdev->mp); | ||
| 1027 | if (IS_ERR(vp)) { | ||
| 1028 | printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); | ||
| 1029 | err = PTR_ERR(vp); | ||
| 1030 | goto err_out_put_mdesc; | ||
| 1031 | } | ||
| 1032 | |||
| 910 | rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); | 1033 | rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); |
| 911 | err = -ENODEV; | 1034 | err = -ENODEV; |
| 912 | if (!rmac) { | 1035 | if (!rmac) { |
| @@ -1025,139 +1148,14 @@ static struct vio_driver vnet_port_driver = { | |||
| 1025 | } | 1148 | } |
| 1026 | }; | 1149 | }; |
| 1027 | 1150 | ||
| 1028 | const char *local_mac_prop = "local-mac-address"; | ||
| 1029 | |||
| 1030 | static int __devinit vnet_probe(struct vio_dev *vdev, | ||
| 1031 | const struct vio_device_id *id) | ||
| 1032 | { | ||
| 1033 | static int vnet_version_printed; | ||
| 1034 | struct mdesc_handle *hp; | ||
| 1035 | struct net_device *dev; | ||
| 1036 | struct vnet *vp; | ||
| 1037 | const u64 *mac; | ||
| 1038 | int err, i, len; | ||
| 1039 | |||
| 1040 | if (vnet_version_printed++ == 0) | ||
| 1041 | printk(KERN_INFO "%s", version); | ||
| 1042 | |||
| 1043 | hp = mdesc_grab(); | ||
| 1044 | |||
| 1045 | mac = mdesc_get_property(hp, vdev->mp, local_mac_prop, &len); | ||
| 1046 | if (!mac) { | ||
| 1047 | printk(KERN_ERR PFX "vnet lacks %s property.\n", | ||
| 1048 | local_mac_prop); | ||
| 1049 | err = -ENODEV; | ||
| 1050 | goto err_out; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | dev = alloc_etherdev(sizeof(*vp)); | ||
| 1054 | if (!dev) { | ||
| 1055 | printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); | ||
| 1056 | err = -ENOMEM; | ||
| 1057 | goto err_out; | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | for (i = 0; i < ETH_ALEN; i++) | ||
| 1061 | dev->dev_addr[i] = (*mac >> (5 - i) * 8) & 0xff; | ||
| 1062 | |||
| 1063 | memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); | ||
| 1064 | |||
| 1065 | SET_NETDEV_DEV(dev, &vdev->dev); | ||
| 1066 | |||
| 1067 | vp = netdev_priv(dev); | ||
| 1068 | |||
| 1069 | spin_lock_init(&vp->lock); | ||
| 1070 | vp->dev = dev; | ||
| 1071 | vp->vdev = vdev; | ||
| 1072 | |||
| 1073 | INIT_LIST_HEAD(&vp->port_list); | ||
| 1074 | for (i = 0; i < VNET_PORT_HASH_SIZE; i++) | ||
| 1075 | INIT_HLIST_HEAD(&vp->port_hash[i]); | ||
| 1076 | |||
| 1077 | dev->open = vnet_open; | ||
| 1078 | dev->stop = vnet_close; | ||
| 1079 | dev->set_multicast_list = vnet_set_rx_mode; | ||
| 1080 | dev->set_mac_address = vnet_set_mac_addr; | ||
| 1081 | dev->tx_timeout = vnet_tx_timeout; | ||
| 1082 | dev->ethtool_ops = &vnet_ethtool_ops; | ||
| 1083 | dev->watchdog_timeo = VNET_TX_TIMEOUT; | ||
| 1084 | dev->change_mtu = vnet_change_mtu; | ||
| 1085 | dev->hard_start_xmit = vnet_start_xmit; | ||
| 1086 | |||
| 1087 | err = register_netdev(dev); | ||
| 1088 | if (err) { | ||
| 1089 | printk(KERN_ERR PFX "Cannot register net device, " | ||
| 1090 | "aborting.\n"); | ||
| 1091 | goto err_out_free_dev; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); | ||
| 1095 | |||
| 1096 | for (i = 0; i < 6; i++) | ||
| 1097 | printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); | ||
| 1098 | |||
| 1099 | dev_set_drvdata(&vdev->dev, vp); | ||
| 1100 | |||
| 1101 | mdesc_release(hp); | ||
| 1102 | |||
| 1103 | return 0; | ||
| 1104 | |||
| 1105 | err_out_free_dev: | ||
| 1106 | free_netdev(dev); | ||
| 1107 | |||
| 1108 | err_out: | ||
| 1109 | mdesc_release(hp); | ||
| 1110 | return err; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | static int vnet_remove(struct vio_dev *vdev) | ||
| 1114 | { | ||
| 1115 | |||
| 1116 | struct vnet *vp = dev_get_drvdata(&vdev->dev); | ||
| 1117 | |||
| 1118 | if (vp) { | ||
| 1119 | /* XXX unregister port, or at least check XXX */ | ||
| 1120 | unregister_netdevice(vp->dev); | ||
| 1121 | dev_set_drvdata(&vdev->dev, NULL); | ||
| 1122 | } | ||
| 1123 | return 0; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | static struct vio_device_id vnet_match[] = { | ||
| 1127 | { | ||
| 1128 | .type = "network", | ||
| 1129 | }, | ||
| 1130 | {}, | ||
| 1131 | }; | ||
| 1132 | MODULE_DEVICE_TABLE(vio, vnet_match); | ||
| 1133 | |||
| 1134 | static struct vio_driver vnet_driver = { | ||
| 1135 | .id_table = vnet_match, | ||
| 1136 | .probe = vnet_probe, | ||
| 1137 | .remove = vnet_remove, | ||
| 1138 | .driver = { | ||
| 1139 | .name = "vnet", | ||
| 1140 | .owner = THIS_MODULE, | ||
| 1141 | } | ||
| 1142 | }; | ||
| 1143 | |||
| 1144 | static int __init vnet_init(void) | 1151 | static int __init vnet_init(void) |
| 1145 | { | 1152 | { |
| 1146 | int err = vio_register_driver(&vnet_driver); | 1153 | return vio_register_driver(&vnet_port_driver); |
| 1147 | |||
| 1148 | if (!err) { | ||
| 1149 | err = vio_register_driver(&vnet_port_driver); | ||
| 1150 | if (err) | ||
| 1151 | vio_unregister_driver(&vnet_driver); | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | return err; | ||
| 1155 | } | 1154 | } |
| 1156 | 1155 | ||
| 1157 | static void __exit vnet_exit(void) | 1156 | static void __exit vnet_exit(void) |
| 1158 | { | 1157 | { |
| 1159 | vio_unregister_driver(&vnet_port_driver); | 1158 | vio_unregister_driver(&vnet_port_driver); |
| 1160 | vio_unregister_driver(&vnet_driver); | ||
| 1161 | } | 1159 | } |
| 1162 | 1160 | ||
| 1163 | module_init(vnet_init); | 1161 | module_init(vnet_init); |
