Merge master.kernel.org:/home/rmk/linux-2.6-arm
[safe/jmp/linux-2.6] / drivers / platform / x86 / sony-laptop.c
index 5837156..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 ***********/
@@ -317,7 +317,8 @@ static void sony_laptop_report_input_event(u8 event)
        struct input_dev *key_dev = sony_laptop_input.key_dev;
        struct sony_laptop_keypress kp = { NULL };
 
-       if (event == SONYPI_EVENT_FNKEY_RELEASED) {
+       if (event == SONYPI_EVENT_FNKEY_RELEASED ||
+                       event == SONYPI_EVENT_ANYBUTTON_RELEASED) {
                /* Nothing, not all VAIOs generate this event */
                return;
        }
@@ -865,7 +866,7 @@ struct sony_nc_event {
 static struct sony_nc_event sony_100_events[] = {
        { 0x90, SONYPI_EVENT_PKEY_P1 },
        { 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED },
-       { 0x91, SONYPI_EVENT_PKEY_P1 },
+       { 0x91, SONYPI_EVENT_PKEY_P2 },
        { 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0x81, SONYPI_EVENT_FNKEY_F1 },
        { 0x01, SONYPI_EVENT_FNKEY_RELEASED },
@@ -905,7 +906,6 @@ static struct sony_nc_event sony_127_events[] = {
        { 0x05, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0x86, SONYPI_EVENT_PKEY_P5 },
        { 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED },
-       { 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0x87, SONYPI_EVENT_SETTINGKEY_PRESSED },
        { 0x07, SONYPI_EVENT_ANYBUTTON_RELEASED },
        { 0, 0 },
@@ -914,13 +914,13 @@ static struct sony_nc_event sony_127_events[] = {
 /*
  * ACPI callbacks
  */
-static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
+static void sony_nc_notify(struct acpi_device *device, u32 event)
 {
        u32 ev = event;
-       int result;
 
        if (ev >= 0x90) {
                /* New-style event */
+               int result;
                int key_handle = 0;
                ev -= 0x90;
 
@@ -929,41 +929,46 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
                if (sony_find_snc_handle(0x127) == ev)
                        key_handle = 0x127;
 
-               if (handle) {
+               if (key_handle) {
                        struct sony_nc_event *key_event;
 
-                       if (sony_call_snc_handle(key_handle, 0x200, &result))
-                               dprintk("sony_acpi_notify, unable to decode"
+                       if (sony_call_snc_handle(key_handle, 0x200, &result)) {
+                               dprintk("sony_nc_notify, unable to decode"
                                        " event 0x%.2x 0x%.2x\n", key_handle,
                                        ev);
-                       else
+                               /* restore the original event */
+                               ev = event;
+                       } else {
                                ev = result & 0xFF;
 
-                       if (key_handle == 0x100)
-                               key_event = sony_100_events;
-                       else
-                               key_event = sony_127_events;
+                               if (key_handle == 0x100)
+                                       key_event = sony_100_events;
+                               else
+                                       key_event = sony_127_events;
 
-                       for (; key_event->data; key_event++) {
-                               if (key_event->data == ev) {
-                                       ev = key_event->event;
-                                       break;
+                               for (; key_event->data; key_event++) {
+                                       if (key_event->data == ev) {
+                                               ev = key_event->event;
+                                               break;
+                                       }
                                }
-                       }
 
-                       if (!key_event->data) {
-                               printk(KERN_INFO DRV_PFX
-                                      "Unknown event: 0x%x 0x%x\n", key_handle,
-                                      ev);
+                               if (!key_event->data)
+                                       printk(KERN_INFO DRV_PFX
+                                                       "Unknown event: 0x%x 0x%x\n",
+                                                       key_handle,
+                                                       ev);
+                               else
+                                       sony_laptop_report_input_event(ev);
                        }
                } else if (sony_find_snc_handle(0x124) == ev) {
                        sony_nc_rfkill_update();
                        return;
                }
-       }
+       } else
+               sony_laptop_report_input_event(ev);
 
-       dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
-       sony_laptop_report_input_event(ev);
+       dprintk("sony_nc_notify, event: 0x%.2x\n", ev);
        acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
 }
 
@@ -971,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;
@@ -999,6 +1001,7 @@ static int sony_nc_function_setup(struct acpi_device *device)
        sony_call_snc_handle(0x0100, 0, &result);
        sony_call_snc_handle(0x0101, 0, &result);
        sony_call_snc_handle(0x0102, 0x100, &result);
+       sony_call_snc_handle(0x0127, 0, &result);
 
        return 0;
 }
@@ -1035,9 +1038,12 @@ static int sony_nc_resume(struct acpi_device *device)
 
        /* set the last requested brightness level */
        if (sony_backlight_device &&
-                       !sony_backlight_update_status(sony_backlight_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;
 }
 
@@ -1045,139 +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;
-       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;
-
-       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;
-}
+       struct rfkill *rfk;
+       enum rfkill_type type;
+       const char *name;
+       int result;
+       bool hwblock;
 
-static int sony_nc_setup_wwan_rfkill(struct acpi_device *device)
-{
-       int err = 0;
-       struct rfkill *sony_wwan_rfkill;
+       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;
+       }
 
-       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;
-}
+       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;
+       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);
        }
 }
 
@@ -1196,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;
 }
@@ -1271,15 +1241,6 @@ static int sony_nc_add(struct acpi_device *device)
                goto outwalk;
        }
 
-       status = acpi_install_notify_handler(sony_nc_acpi_handle,
-                                            ACPI_DEVICE_NOTIFY,
-                                            sony_acpi_notify, NULL);
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status);
-               result = -ENODEV;
-               goto outinput;
-       }
-
        if (acpi_video_backlight_support()) {
                printk(KERN_INFO DRV_PFX "brightness ignored, must be "
                       "controlled by ACPI video driver\n");
@@ -1357,13 +1318,6 @@ static int sony_nc_add(struct acpi_device *device)
        if (sony_backlight_device)
                backlight_device_unregister(sony_backlight_device);
 
-       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
-                                           ACPI_DEVICE_NOTIFY,
-                                           sony_acpi_notify);
-       if (ACPI_FAILURE(status))
-               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
-
-      outinput:
        sony_laptop_remove_input();
 
       outwalk:
@@ -1373,7 +1327,6 @@ static int sony_nc_add(struct acpi_device *device)
 
 static int sony_nc_remove(struct acpi_device *device, int type)
 {
-       acpi_status status;
        struct sony_nc_value *item;
 
        if (sony_backlight_device)
@@ -1381,12 +1334,6 @@ static int sony_nc_remove(struct acpi_device *device, int type)
 
        sony_nc_acpi_device = NULL;
 
-       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
-                                           ACPI_DEVICE_NOTIFY,
-                                           sony_acpi_notify);
-       if (ACPI_FAILURE(status))
-               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
-
        for (item = sony_nc_values; item->name; ++item) {
                device_remove_file(&sony_pf_device->dev, &item->devattr);
        }
@@ -1420,6 +1367,7 @@ static struct acpi_driver sony_nc_driver = {
                .add = sony_nc_add,
                .remove = sony_nc_remove,
                .resume = sony_nc_resume,
+               .notify = sony_nc_notify,
                },
 };
 
@@ -2162,12 +2110,7 @@ static struct sonypi_compat_s sonypi_compat = {
 
 static int sonypi_misc_fasync(int fd, struct file *filp, int on)
 {
-       int retval;
-
-       retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
-       if (retval < 0)
-               return retval;
-       return 0;
+       return fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
 }
 
 static int sonypi_misc_release(struct inode *inode, struct file *file)