[Cc-ing more people who could test the patch on different models] On Mon, Mar 23, 2009 at 07:51:18PM +0000, Matthew Garrett wrote: > Ok, and here's what I consider to be the "final" version for now. It > cleans up the event handling (it turns out that the event value > corresponds to the SN00 value required to call the corresponding > function) and adds support for the external switch to flag the devices > as being in HARD_BLOCKED state. Tested on a P series (remotely), if > someone could check it on a Z and ideally a TT, that would be great. I > think it's otherwise basically complete for keyboard and rfkill > handling. Looks good to me. I asked Michael to test one of you earlier versions on his TT and he seems to be happy about it (apart from an oops somewhere in the tty_release path[1] which is unlikely to be related to sony-laptop). Please send your series in, I'll then rebase my patches and push to Len. [1]: http://bugzilla.kernel.org/attachment.cgi?id=20651 > > diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c > index 537959d..2d142d3 100644 > --- a/drivers/platform/x86/sony-laptop.c > +++ b/drivers/platform/x86/sony-laptop.c > @@ -64,6 +64,7 @@ > #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> > @@ -123,6 +124,18 @@ MODULE_PARM_DESC(minor, > "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 > @@ -134,6 +147,7 @@ struct sony_laptop_input_s { > spinlock_t fifo_lock; > struct workqueue_struct *wq; > }; > + > static struct sony_laptop_input_s sony_laptop_input = { > .users = ATOMIC_INIT(0), > }; > @@ -211,6 +225,7 @@ static int sony_laptop_input_index[] = { > 48, /* 61 SONYPI_EVENT_WIRELESS_OFF */ > 49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */ > 50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */ > + 51, /* 64 SONYPI_EVENT_CD_EJECT_PRESSED */ > }; > > static int sony_laptop_input_keycode_map[] = { > @@ -264,7 +279,8 @@ static int sony_laptop_input_keycode_map[] = { > KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */ > KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */ > KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */ > - KEY_ZOOMOUT /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */ > + KEY_ZOOMOUT, /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */ > + KEY_EJECTCD /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */ > }; > > /* release buttons after a short delay if pressed */ > @@ -689,6 +705,31 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, > return -1; > } > > +static int sony_find_snc_handle(int handle) > +{ > + int i; > + int result; > + > + for (i=0x20; i<0x30; i++) { > + acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result); > + if (result == handle) > + return i-0x20; > + } > + > + return -1; > +} > + > +static int sony_call_snc_handle(int handle, int argument, int *result) > +{ > + int offset = sony_find_snc_handle(handle); > + > + if (offset < 0) > + return -1; > + > + return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, > + result); > +} > + > /* > * sony_nc_values input/output validate functions > */ > @@ -809,33 +850,11 @@ struct sony_nc_event { > u8 event; > }; > > -static struct sony_nc_event *sony_nc_events; > - > -/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence > - * for Fn keys > - */ > -static int sony_nc_C_enable(const struct dmi_system_id *id) > -{ > - int result = 0; > - > - printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident); > - > - sony_nc_events = id->driver_data; > - > - if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0 > - || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0 > - || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0 > - || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0 > - || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0 > - || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) { > - printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some " > - "functionalities may be missing\n"); > - return 1; > - } > - return 0; > -} > - > -static struct sony_nc_event sony_C_events[] = { > +static struct sony_nc_event sony_nc_events[] = { > + { 0x90, SONYPI_EVENT_PKEY_P1 }, > + { 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED }, > + { 0x91, SONYPI_EVENT_PKEY_P1 }, > + { 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED }, > { 0x81, SONYPI_EVENT_FNKEY_F1 }, > { 0x01, SONYPI_EVENT_FNKEY_RELEASED }, > { 0x85, SONYPI_EVENT_FNKEY_F5 }, > @@ -844,88 +863,53 @@ static struct sony_nc_event sony_C_events[] = { > { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, > { 0x87, SONYPI_EVENT_FNKEY_F7 }, > { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, > + { 0x89, SONYPI_EVENT_FNKEY_F9 }, > + { 0x09, SONYPI_EVENT_FNKEY_RELEASED }, > { 0x8A, SONYPI_EVENT_FNKEY_F10 }, > { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, > { 0x8C, SONYPI_EVENT_FNKEY_F12 }, > { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, > + { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, > + { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, > { 0, 0 }, > }; > > -/* SNC-only model map */ > -static const struct dmi_system_id sony_nc_ids[] = { > - { > - .ident = "Sony Vaio FE Series", > - .callback = sony_nc_C_enable, > - .driver_data = sony_C_events, > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), > - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"), > - }, > - }, > - { > - .ident = "Sony Vaio FZ Series", > - .callback = sony_nc_C_enable, > - .driver_data = sony_C_events, > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), > - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ"), > - }, > - }, > - { > - .ident = "Sony Vaio C Series", > - .callback = sony_nc_C_enable, > - .driver_data = sony_C_events, > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), > - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"), > - }, > - }, > - { > - .ident = "Sony Vaio N Series", > - .callback = sony_nc_C_enable, > - .driver_data = sony_C_events, > - .matches = { > - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), > - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"), > - }, > - }, > - { } > -}; > - > /* > * ACPI callbacks > */ > static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) > { > - struct sony_nc_event *evmap; > u32 ev = event; > int result; > > - if (ev == 0x92) { > - /* read the key pressed from EC.GECR > - * A call to SN07 with 0x0202 will do it as well respecting > - * the current protocol on different OSes > - * > - * Note: the path for GECR may be > - * \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends) > - * \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR) > - * > - * TODO: we may want to do the same for the older GHKE -need > - * dmi list- so this snippet may become one more callback. > - */ > - if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0) > - dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev); > - else > - ev = result & 0xFF; > - } > + if (ev >= 0x90) { > + /* New-style event */ > + int origev = ev; > + ev -= 0x90; > > - if (sony_nc_events) > - for (evmap = sony_nc_events; evmap->event; evmap++) { > - if (evmap->data == ev) { > - ev = evmap->event; > - break; > + if (sony_find_snc_handle(0x100) == ev) { > + int i; > + > + if (sony_call_snc_handle(0x100, 0x200, &result)) > + dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev); > + else > + ev = result & 0xFF; > + > + for (i=0; sony_nc_events[i].event; i++) { > + if (sony_nc_events[i].data == ev) { > + ev = sony_nc_events[i].event; > + break; > + } > } > + > + 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; > } > + } > > dprintk("sony_acpi_notify, event: 0x%.2x\n", ev); > sony_laptop_report_input_event(ev); > @@ -953,9 +937,24 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, > /* > * ACPI device > */ > +static int sony_nc_function_setup(struct acpi_device *device) { > + int result; > + > + /* Enable all events */ > + acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result); > + > + /* Setup hotkeys */ > + sony_call_snc_handle(0x0100, 0, &result); > + sony_call_snc_handle(0x0101, 0, &result); > + sony_call_snc_handle(0x0102, 0x100, &result); > + > + return 0; > +} > + > static int sony_nc_resume(struct acpi_device *device) > { > struct sony_nc_value *item; > + acpi_handle handle; > > for (item = sony_nc_values; item->name; item++) { > int ret; > @@ -970,13 +969,188 @@ static int sony_nc_resume(struct acpi_device *device) > } > } > > + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", > + &handle))) { > + if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) > + dprintk("ECON Method failed\n"); > + } > + > + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", > + &handle))) { > + dprintk("Doing SNC setup\n"); > + sony_nc_function_setup(device); > + } > + > /* set the last requested brightness level */ > if (sony_backlight_device && > !sony_backlight_update_status(sony_backlight_device)) > printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n"); > > - /* re-initialize models with specific requirements */ > - dmi_check_system(sony_nc_ids); > + 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; > } > @@ -1024,6 +1198,19 @@ static int sony_nc_add(struct acpi_device *device) > dprintk("_INI Method failed\n"); > } > > + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", > + &handle))) { > + if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) > + dprintk("ECON Method failed\n"); > + } > + > + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", > + &handle))) { > + dprintk("Doing SNC setup\n"); > + sony_nc_function_setup(device); > + sony_nc_rfkill_setup(device); > + } > + > /* setup input devices and helper fifo */ > result = sony_laptop_setup_input(device); > if (result) { > @@ -1063,9 +1250,6 @@ static int sony_nc_add(struct acpi_device *device) > > } > > - /* initialize models with specific requirements */ > - dmi_check_system(sony_nc_ids); > - > result = sony_pf_add(); > if (result) > goto outbacklight; > @@ -1131,6 +1315,7 @@ static int sony_nc_add(struct acpi_device *device) > sony_laptop_remove_input(); > > outwalk: > + sony_nc_rfkill_cleanup(); > return result; > } > > @@ -1156,6 +1341,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) > > sony_pf_remove(); > sony_laptop_remove_input(); > + sony_nc_rfkill_cleanup(); > dprintk(SONY_NC_DRIVER_NAME " removed.\n"); > > return 0; > diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h > index f41ffd7..8458dbe 100644 > --- a/include/linux/sonypi.h > +++ b/include/linux/sonypi.h > @@ -103,6 +103,7 @@ > #define SONYPI_EVENT_WIRELESS_OFF 61 > #define SONYPI_EVENT_ZOOM_IN_PRESSED 62 > #define SONYPI_EVENT_ZOOM_OUT_PRESSED 63 > +#define SONYPI_EVENT_CD_EJECT_PRESSED 64 > > /* get/set brightness */ > #define SONYPI_IOCGBRT _IOR('v', 0, __u8) > > > -- > Matthew Garrett | mjg59@xxxxxxxxxxxxx > -- mattia :wq! -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html