#include <asm/uaccess.h>
 #include <linux/sonypi.h>
 #include <linux/sony-laptop.h>
+#include <linux/rfkill.h>
 #ifdef CONFIG_SONYPI_COMPAT
 #include <linux/poll.h>
 #include <linux/miscdevice.h>
                 "default is -1 (automatic)");
 #endif
 
+enum sony_nc_rfkill {
+       SONY_WIFI,
+       SONY_BLUETOOTH,
+       SONY_WWAN,
+       SONY_WIMAX,
+       SONY_RFKILL_MAX,
+};
+
+static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
+static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
+static void sony_nc_rfkill_update(void);
+
 /*********** Input Devices ***********/
 
 #define SONY_LAPTOP_BUF_SIZE   128
        spinlock_t              fifo_lock;
        struct workqueue_struct *wq;
 };
+
 static struct sony_laptop_input_s sony_laptop_input = {
        .users = ATOMIC_INIT(0),
 };
                        if (!sony_nc_events[i].data)
                                printk(KERN_INFO DRV_PFX
                                       "Unknown event: %x %x\n", origev, ev);
+               } else if (sony_find_snc_handle(0x124) == ev) {
+                       sony_nc_rfkill_update();
+                       return;
                }
        }
 
        return 0;
 }
 
+static void sony_nc_rfkill_cleanup(void)
+{
+       int i;
+
+       for (i = 0; i < SONY_RFKILL_MAX; i++) {
+               if (sony_rfkill_devices[i])
+                       rfkill_unregister(sony_rfkill_devices[i]);
+       }
+}
+
+static int sony_nc_rfkill_get(void *data, enum rfkill_state *state)
+{
+       int result;
+       int argument = sony_rfkill_address[(long) data];
+
+       sony_call_snc_handle(0x124, 0x200, &result);
+       if (result & 0x1) {
+               sony_call_snc_handle(0x124, argument, &result);
+               if (result & 0xf)
+                       *state = RFKILL_STATE_UNBLOCKED;
+               else
+                       *state = RFKILL_STATE_SOFT_BLOCKED;
+       } else {
+               *state = RFKILL_STATE_HARD_BLOCKED;
+       }
+
+       return 0;
+}
+
+static int sony_nc_rfkill_set(void *data, enum rfkill_state state)
+{
+       int result;
+       int argument = sony_rfkill_address[(long) data] + 0x100;
+
+       if (state == RFKILL_STATE_UNBLOCKED)
+               argument |= 0xff0000;
+
+       return sony_call_snc_handle(0x124, argument, &result);
+}
+
+static int sony_nc_setup_wifi_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_wifi_rfkill;
+
+       sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
+       if (!sony_wifi_rfkill)
+               return -1;
+       sony_wifi_rfkill->name = "sony-wifi";
+       sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_wifi_rfkill->get_state = sony_nc_rfkill_get;
+       sony_wifi_rfkill->user_claim_unsupported = 1;
+       sony_wifi_rfkill->data = (void *)SONY_WIFI;
+       err = rfkill_register(sony_wifi_rfkill);
+       if (err)
+               rfkill_free(sony_wifi_rfkill);
+       else
+               sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill;
+       return err;
+}
+
+static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_bluetooth_rfkill;
+
+       sony_bluetooth_rfkill = rfkill_allocate(&device->dev,
+                                               RFKILL_TYPE_BLUETOOTH);
+       if (!sony_bluetooth_rfkill)
+               return -1;
+       sony_bluetooth_rfkill->name = "sony-bluetooth";
+       sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get;
+       sony_bluetooth_rfkill->user_claim_unsupported = 1;
+       sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH;
+       err = rfkill_register(sony_bluetooth_rfkill);
+       if (err)
+               rfkill_free(sony_bluetooth_rfkill);
+       else
+               sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill;
+       return err;
+}
+
+static int sony_nc_setup_wwan_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_wwan_rfkill;
+
+       sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
+       if (!sony_wwan_rfkill)
+               return -1;
+       sony_wwan_rfkill->name = "sony-wwan";
+       sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_wwan_rfkill->get_state = sony_nc_rfkill_get;
+       sony_wwan_rfkill->user_claim_unsupported = 1;
+       sony_wwan_rfkill->data = (void *)SONY_WWAN;
+       err = rfkill_register(sony_wwan_rfkill);
+       if (err)
+               rfkill_free(sony_wwan_rfkill);
+       else
+               sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill;
+       return err;
+}
+
+static int sony_nc_setup_wimax_rfkill(struct acpi_device *device)
+{
+       int err = 0;
+       struct rfkill *sony_wimax_rfkill;
+
+       sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
+       if (!sony_wimax_rfkill)
+               return -1;
+       sony_wimax_rfkill->name = "sony-wimax";
+       sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set;
+       sony_wimax_rfkill->get_state = sony_nc_rfkill_get;
+       sony_wimax_rfkill->user_claim_unsupported = 1;
+       sony_wimax_rfkill->data = (void *)SONY_WIMAX;
+       err = rfkill_register(sony_wimax_rfkill);
+       if (err)
+               rfkill_free(sony_wimax_rfkill);
+       else
+               sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill;
+       return err;
+}
+
+static void sony_nc_rfkill_update()
+{
+       int i;
+       enum rfkill_state state;
+
+       for (i = 0; i < SONY_RFKILL_MAX; i++) {
+               if (sony_rfkill_devices[i]) {
+                       sony_rfkill_devices[i]->
+                               get_state(sony_rfkill_devices[i]->data,
+                                         &state);
+                       rfkill_force_state(sony_rfkill_devices[i], state);
+               }
+       }
+}
+
+static int sony_nc_rfkill_setup(struct acpi_device *device)
+{
+       int result, ret;
+
+       if (sony_find_snc_handle(0x124) == -1)
+               return -1;
+
+       ret = sony_call_snc_handle(0x124, 0xb00, &result);
+       if (ret) {
+               printk(KERN_INFO DRV_PFX
+                      "Unable to enumerate rfkill devices: %x\n", ret);
+               return ret;
+       }
+
+       if (result & 0x1)
+               sony_nc_setup_wifi_rfkill(device);
+       if (result & 0x2)
+               sony_nc_setup_bluetooth_rfkill(device);
+       if (result & 0x1c)
+               sony_nc_setup_wwan_rfkill(device);
+       if (result & 0x20)
+               sony_nc_setup_wimax_rfkill(device);
+
+       return 0;
+}
+
 static int sony_nc_add(struct acpi_device *device)
 {
        acpi_status status;
                                         &handle))) {
                dprintk("Doing SNC setup\n");
                sony_nc_function_setup(device);
+               sony_nc_rfkill_setup(device);
        }
 
        /* setup input devices and helper fifo */
        sony_laptop_remove_input();
 
       outwalk:
+       sony_nc_rfkill_cleanup();
        return result;
 }
 
 
        sony_pf_remove();
        sony_laptop_remove_input();
+       sony_nc_rfkill_cleanup();
        dprintk(SONY_NC_DRIVER_NAME " removed.\n");
 
        return 0;