Merge master.kernel.org:/home/rmk/linux-2.6-arm
[safe/jmp/linux-2.6] / drivers / platform / x86 / sony-laptop.c
index 5529585..afdbdaa 100644 (file)
@@ -128,11 +128,11 @@ enum sony_nc_rfkill {
        SONY_BLUETOOTH,
        SONY_WWAN,
        SONY_WIMAX,
-       SONY_RFKILL_MAX,
+       N_SONY_RFKILL,
 };
 
-static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX];
-static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900};
+static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];
+static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};
 static void sony_nc_rfkill_update(void);
 
 /*********** Input Devices ***********/
@@ -976,15 +976,12 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
                                      void *context, void **return_value)
 {
        struct acpi_device_info *info;
-       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-
-       if (ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) {
-               info = buffer.pointer;
 
+       if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) {
                printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n",
                        (char *)&info->name, info->param_count);
 
-               kfree(buffer.pointer);
+               kfree(info);
        }
 
        return AE_OK;
@@ -1044,6 +1041,9 @@ static int sony_nc_resume(struct acpi_device *device)
                        sony_backlight_update_status(sony_backlight_device) < 0)
                printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
 
+       /* re-read rfkill state */
+       sony_nc_rfkill_update();
+
        return 0;
 }
 
@@ -1051,151 +1051,103 @@ static void sony_nc_rfkill_cleanup(void)
 {
        int i;
 
-       for (i = 0; i < SONY_RFKILL_MAX; i++) {
-               if (sony_rfkill_devices[i])
+       for (i = 0; i < N_SONY_RFKILL; i++) {
+               if (sony_rfkill_devices[i]) {
                        rfkill_unregister(sony_rfkill_devices[i]);
+                       rfkill_destroy(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)
+static int sony_nc_rfkill_set(void *data, bool blocked)
 {
        int result;
        int argument = sony_rfkill_address[(long) data] + 0x100;
 
-       if (state == RFKILL_STATE_UNBLOCKED)
+       if (!blocked)
                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;
-               sony_nc_rfkill_set(sony_wifi_rfkill->data,
-                               RFKILL_STATE_UNBLOCKED);
-       }
-       return err;
-}
+static const struct rfkill_ops sony_rfkill_ops = {
+       .set_block = sony_nc_rfkill_set,
+};
 
-static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device)
+static int sony_nc_setup_rfkill(struct acpi_device *device,
+                               enum sony_nc_rfkill nc_type)
 {
        int err = 0;
-       struct rfkill *sony_bluetooth_rfkill;
+       struct rfkill *rfk;
+       enum rfkill_type type;
+       const char *name;
+       int result;
+       bool hwblock;
 
-       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;
-               sony_nc_rfkill_set(sony_bluetooth_rfkill->data,
-                               RFKILL_STATE_UNBLOCKED);
+       switch (nc_type) {
+       case SONY_WIFI:
+               type = RFKILL_TYPE_WLAN;
+               name = "sony-wifi";
+               break;
+       case SONY_BLUETOOTH:
+               type = RFKILL_TYPE_BLUETOOTH;
+               name = "sony-bluetooth";
+               break;
+       case SONY_WWAN:
+               type = RFKILL_TYPE_WWAN;
+               name = "sony-wwan";
+               break;
+       case SONY_WIMAX:
+               type = RFKILL_TYPE_WIMAX;
+               name = "sony-wimax";
+               break;
+       default:
+               return -EINVAL;
        }
-       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;
-               sony_nc_rfkill_set(sony_wwan_rfkill->data,
-                               RFKILL_STATE_UNBLOCKED);
-       }
-       return err;
-}
+       rfk = rfkill_alloc(name, &device->dev, type,
+                          &sony_rfkill_ops, (void *)nc_type);
+       if (!rfk)
+               return -ENOMEM;
 
-static int sony_nc_setup_wimax_rfkill(struct acpi_device *device)
-{
-       int err = 0;
-       struct rfkill *sony_wimax_rfkill;
+       sony_call_snc_handle(0x124, 0x200, &result);
+       hwblock = !(result & 0x1);
+       rfkill_set_hw_state(rfk, hwblock);
 
-       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;
-               sony_nc_rfkill_set(sony_wimax_rfkill->data,
-                               RFKILL_STATE_UNBLOCKED);
+       err = rfkill_register(rfk);
+       if (err) {
+               rfkill_destroy(rfk);
+               return err;
        }
+       sony_rfkill_devices[nc_type] = rfk;
        return err;
 }
 
 static void sony_nc_rfkill_update()
 {
-       int i;
-       enum rfkill_state state;
+       enum sony_nc_rfkill i;
+       int result;
+       bool hwblock;
 
-       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);
+       sony_call_snc_handle(0x124, 0x200, &result);
+       hwblock = !(result & 0x1);
+
+       for (i = 0; i < N_SONY_RFKILL; i++) {
+               int argument = sony_rfkill_address[i];
+
+               if (!sony_rfkill_devices[i])
+                       continue;
+
+               if (hwblock) {
+                       if (rfkill_set_hw_state(sony_rfkill_devices[i], true)) {
+                               /* we already know we're blocked */
+                       }
+                       continue;
                }
+
+               sony_call_snc_handle(0x124, argument, &result);
+               rfkill_set_states(sony_rfkill_devices[i],
+                                 !(result & 0xf), false);
        }
 }
 
@@ -1214,13 +1166,13 @@ static int sony_nc_rfkill_setup(struct acpi_device *device)
        }
 
        if (result & 0x1)
-               sony_nc_setup_wifi_rfkill(device);
+               sony_nc_setup_rfkill(device, SONY_WIFI);
        if (result & 0x2)
-               sony_nc_setup_bluetooth_rfkill(device);
+               sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
        if (result & 0x1c)
-               sony_nc_setup_wwan_rfkill(device);
+               sony_nc_setup_rfkill(device, SONY_WWAN);
        if (result & 0x20)
-               sony_nc_setup_wimax_rfkill(device);
+               sony_nc_setup_rfkill(device, SONY_WIMAX);
 
        return 0;
 }