[POWERPC] PS3: Gelic network driver Wake-on-LAN support
Masakazu Mokuno [Thu, 27 Mar 2008 00:39:31 +0000 (11:39 +1100)]
Add Wake-on-LAN support to the PS3 Gelic network driver.
Other OS WOL support was introduced in PS3 system firmware 2.20.

Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>

drivers/net/ps3_gelic_net.c
drivers/net/ps3_gelic_net.h

index 7eb6e7e..e365efb 100644 (file)
@@ -1266,6 +1266,85 @@ int gelic_net_set_rx_csum(struct net_device *netdev, u32 data)
        return 0;
 }
 
+static void gelic_net_get_wol(struct net_device *netdev,
+                             struct ethtool_wolinfo *wol)
+{
+       if (0 <= ps3_compare_firmware_version(2, 2, 0))
+               wol->supported = WAKE_MAGIC;
+       else
+               wol->supported = 0;
+
+       wol->wolopts = ps3_sys_manager_get_wol() ? wol->supported : 0;
+       memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+static int gelic_net_set_wol(struct net_device *netdev,
+                            struct ethtool_wolinfo *wol)
+{
+       int status;
+       struct gelic_card *card;
+       u64 v1, v2;
+
+       if (ps3_compare_firmware_version(2, 2, 0) < 0 ||
+           !capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       if (wol->wolopts & ~WAKE_MAGIC)
+               return -EINVAL;
+
+       card = netdev_card(netdev);
+       if (wol->wolopts & WAKE_MAGIC) {
+               status = lv1_net_control(bus_id(card), dev_id(card),
+                                        GELIC_LV1_SET_WOL,
+                                        GELIC_LV1_WOL_MAGIC_PACKET,
+                                        0, GELIC_LV1_WOL_MP_ENABLE,
+                                        &v1, &v2);
+               if (status) {
+                       pr_info("%s: enabling WOL failed %d\n", __func__,
+                               status);
+                       status = -EIO;
+                       goto done;
+               }
+               status = lv1_net_control(bus_id(card), dev_id(card),
+                                        GELIC_LV1_SET_WOL,
+                                        GELIC_LV1_WOL_ADD_MATCH_ADDR,
+                                        0, GELIC_LV1_WOL_MATCH_ALL,
+                                        &v1, &v2);
+               if (!status)
+                       ps3_sys_manager_set_wol(1);
+               else {
+                       pr_info("%s: enabling WOL filter failed %d\n",
+                               __func__, status);
+                       status = -EIO;
+               }
+       } else {
+               status = lv1_net_control(bus_id(card), dev_id(card),
+                                        GELIC_LV1_SET_WOL,
+                                        GELIC_LV1_WOL_MAGIC_PACKET,
+                                        0, GELIC_LV1_WOL_MP_DISABLE,
+                                        &v1, &v2);
+               if (status) {
+                       pr_info("%s: disabling WOL failed %d\n", __func__,
+                               status);
+                       status = -EIO;
+                       goto done;
+               }
+               status = lv1_net_control(bus_id(card), dev_id(card),
+                                        GELIC_LV1_SET_WOL,
+                                        GELIC_LV1_WOL_DELETE_MATCH_ADDR,
+                                        0, GELIC_LV1_WOL_MATCH_ALL,
+                                        &v1, &v2);
+               if (!status)
+                       ps3_sys_manager_set_wol(0);
+               else {
+                       pr_info("%s: removing WOL filter failed %d\n",
+                               __func__, status);
+                       status = -EIO;
+               }
+       }
+done:
+       return status;
+}
+
 static struct ethtool_ops gelic_ether_ethtool_ops = {
        .get_drvinfo    = gelic_net_get_drvinfo,
        .get_settings   = gelic_ether_get_settings,
@@ -1274,6 +1353,8 @@ static struct ethtool_ops gelic_ether_ethtool_ops = {
        .set_tx_csum    = ethtool_op_set_tx_csum,
        .get_rx_csum    = gelic_net_get_rx_csum,
        .set_rx_csum    = gelic_net_set_rx_csum,
+       .get_wol        = gelic_net_get_wol,
+       .set_wol        = gelic_net_set_wol,
 };
 
 /**
index 1d39d06..520f143 100644 (file)
@@ -182,12 +182,32 @@ enum gelic_lv1_net_control_code {
        GELIC_LV1_GET_ETH_PORT_STATUS   = 2,
        GELIC_LV1_SET_NEGOTIATION_MODE  = 3,
        GELIC_LV1_GET_VLAN_ID           = 4,
+       GELIC_LV1_SET_WOL               = 5,
        GELIC_LV1_GET_CHANNEL           = 6,
        GELIC_LV1_POST_WLAN_CMD         = 9,
        GELIC_LV1_GET_WLAN_CMD_RESULT   = 10,
        GELIC_LV1_GET_WLAN_EVENT        = 11
 };
 
+/* for GELIC_LV1_SET_WOL */
+enum gelic_lv1_wol_command {
+       GELIC_LV1_WOL_MAGIC_PACKET      = 1,
+       GELIC_LV1_WOL_ADD_MATCH_ADDR    = 6,
+       GELIC_LV1_WOL_DELETE_MATCH_ADDR = 7,
+};
+
+/* for GELIC_LV1_WOL_MAGIC_PACKET */
+enum gelic_lv1_wol_mp_arg {
+       GELIC_LV1_WOL_MP_DISABLE        = 0,
+       GELIC_LV1_WOL_MP_ENABLE         = 1,
+};
+
+/* for GELIC_LV1_WOL_{ADD,DELETE}_MATCH_ADDR */
+enum gelic_lv1_wol_match_arg {
+       GELIC_LV1_WOL_MATCH_INDIVIDUAL  = 0,
+       GELIC_LV1_WOL_MATCH_ALL         = 1,
+};
+
 /* status returened from GET_ETH_PORT_STATUS */
 enum gelic_lv1_ether_port_status {
        GELIC_LV1_ETHER_LINK_UP         = 0x0000000000000001L,