Пример #1
0
static void bbswitch_off(void) {
    if (is_card_disabled())
        return;

    // to prevent the system from possibly locking up, don't disable the device
    // if it's still in use by a driver (i.e. nouveau or nvidia)
    if (dis_dev->driver) {
        pr_warn("device %s is in use by driver '%s', refusing OFF\n",
            dev_name(&dis_dev->dev), dis_dev->driver->name);
        return;
    }

    pr_info("disabling discrete graphics\n");

    if (bbswitch_optimus_dsm()) {
        pr_warn("Optimus ACPI call failed, the device is not disabled\n");
        return;
    }

    pci_save_state(dis_dev);
    pci_clear_master(dis_dev);
    pci_disable_device(dis_dev);
    pci_set_power_state(dis_dev, PCI_D3cold);

    if (bbswitch_acpi_off())
        pr_warn("The discrete card could not be disabled by a _DSM call\n");
}
Пример #2
0
static int bbswitch_pm_handler(struct notifier_block *nbp,
    unsigned long event_type, void *p) {
    switch (event_type) {
    case PM_HIBERNATION_PREPARE:
    case PM_SUSPEND_PREPARE:
        dis_dev_get();
        dis_before_suspend_disabled = is_card_disabled();
        // enable the device before suspend to avoid the PCI config space from
        // being saved incorrectly
        if (dis_before_suspend_disabled)
            bbswitch_on();
        dis_dev_put();
        break;
    case PM_POST_HIBERNATION:
    case PM_POST_SUSPEND:
    case PM_POST_RESTORE:
        // after suspend, the card is on, but if it was off before suspend,
        // disable it again
        if (dis_before_suspend_disabled) {
            dis_dev_get();
            bbswitch_off();
            dis_dev_put();
        }
        break;
    case PM_RESTORE_PREPARE:
        // deliberately don't do anything as it does not occur before suspend
        // nor hibernate, but before restoring a saved image. In that case,
        // either PM_POST_HIBERNATION or PM_POST_RESTORE will be called
        break;
    }
    return 0;
}
Пример #3
0
static void bbswitch_off(void) {
    if (is_card_disabled())
        return;

    // to prevent the system from possibly locking up, don't disable the device
    // if it's still in use by a driver (i.e. nouveau or nvidia)
    if (dis_dev->driver) {
        printk(KERN_WARNING "bbswitch: device %s is in use by driver '%s', "
            "refusing OFF\n", dev_name(&dis_dev->dev), dis_dev->driver->name);
        return;
    }

    printk(KERN_INFO "bbswitch: disabling discrete graphics\n");

    if (dsm_type == DSM_TYPE_OPTIMUS && bbswitch_optimus_dsm()) {
        printk(KERN_WARNING "bbswitch: ACPI call failed, the device is not"
            " disabled\n");
        return;
    }

    pci_save_state(dis_dev);
    pci_clear_master(dis_dev);
    pci_disable_device(dis_dev);
    pci_set_power_state(dis_dev, PCI_D3hot);

    if (bbswitch_acpi_off())
        printk(KERN_WARNING "bbswitch: The discrete card could not be disabled"
                " by a _DSM call\n");
}
Пример #4
0
static int bbswitch_proc_show(struct seq_file *seqfp, void *p) {
    // show the card state. Example output: 0000:01:00:00 ON
    dis_dev_get();
    seq_printf(seqfp, "%s %s\n", dev_name(&dis_dev->dev),
             is_card_disabled() ? "OFF" : "ON");
    dis_dev_put();
    return 0;
}
Пример #5
0
static int __init bbswitch_init(void) {
    struct proc_dir_entry *acpi_entry;
    struct pci_dev *pdev = NULL;
    int class = PCI_CLASS_DISPLAY_VGA << 8;

    while ((pdev = pci_get_class(class, pdev)) != NULL) {
        struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
        acpi_handle handle;

        handle = DEVICE_ACPI_HANDLE(&pdev->dev);
        if (!handle)
            continue;

        if (pdev->vendor != PCI_VENDOR_ID_INTEL) {
            dis_dev = pdev;
            dis_handle = handle;
            acpi_get_name(handle, ACPI_FULL_PATHNAME, &buf);
            printk(KERN_INFO "bbswitch: Found discrete VGA device %s: %s\n",
                dev_name(&pdev->dev), (char *)buf.pointer);
        }
        kfree(buf.pointer);
    }

    if (dis_dev == NULL) {
        printk(KERN_ERR "bbswitch: No discrete VGA device found\n");
        return -ENODEV;
    }

    if (has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) {
        dsm_type = DSM_TYPE_OPTIMUS;
        printk(KERN_INFO "bbswitch: detected an Optimus _DSM function\n");
    } else if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) {
        dsm_type = DSM_TYPE_NVIDIA;
        printk(KERN_INFO "bbswitch: detected a nVidia _DSM function\n");
    } else {
        printk(KERN_ERR "bbswitch: No suitable _DSM call found.\n");
        return -ENODEV;
    }

    acpi_entry = create_proc_entry("bbswitch", 0660, acpi_root_dir);
    if (acpi_entry == NULL) {
        printk(KERN_ERR "bbswitch: Couldn't create proc entry\n");
        return -ENOMEM;
    }

    printk(KERN_INFO "bbswitch: Succesfully loaded. Discrete card %s is %s\n",
        dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");

    acpi_entry->write_proc = bbswitch_write;
    acpi_entry->read_proc = bbswitch_read;

    nb.notifier_call = &bbswitch_pm_handler;
    register_pm_notifier(&nb);

    return 0;
}
Пример #6
0
static void bbswitch_on(void) {
    if (!is_card_disabled())
        return;

    pr_info("enabling discrete graphics\n");

    if (bbswitch_acpi_on())
        pr_warn("The discrete card could not be enabled by a _DSM call\n");

    pci_set_power_state(dis_dev, PCI_D0);
    pci_restore_state(dis_dev);
    if (pci_enable_device(dis_dev))
        pr_warn("failed to enable %s\n", dev_name(&dis_dev->dev));
    pci_set_master(dis_dev);
}
Пример #7
0
static void bbswitch_on(void) {
    if (!is_card_disabled())
        return;

    printk(KERN_INFO "bbswitch: enabling discrete graphics\n");

    if (bbswitch_acpi_on())
        printk(KERN_WARNING "bbswitch: The discrete card could not be enabled"
            " by a _DSM call\n");

    pci_set_power_state(dis_dev, PCI_D0);
    pci_restore_state(dis_dev);
    if (pci_enable_device(dis_dev))
        printk(KERN_WARNING "bbswitch: failed to enable %s\n",
            dev_name(&dis_dev->dev));
    pci_set_master(dis_dev);
}
Пример #8
0
static void __exit bbswitch_exit(void) {
    remove_proc_entry("bbswitch", acpi_root_dir);

    dis_dev_get();

    if (unload_state == CARD_ON)
        bbswitch_on();
    else if (unload_state == CARD_OFF)
        bbswitch_off();

    pr_info("Unloaded. Discrete card %s is %s\n",
        dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");

    dis_dev_put();

    if (nb.notifier_call)
        unregister_pm_notifier(&nb);
}
Пример #9
0
static void bbswitch_off(void) {
    if (is_card_disabled())
        return;

    // to prevent the system from possibly locking up, don't disable the device
    // if it's still in use by a driver (i.e. nouveau or nvidia)
    if (dis_dev->driver) {
        pr_warn("device %s is in use by driver '%s', refusing OFF\n",
            dev_name(&dis_dev->dev), dis_dev->driver->name);
        return;
    }

    pr_info("disabling discrete graphics\n");

    if (bbswitch_optimus_dsm()) {
        pr_warn("Optimus ACPI call failed, the device is not disabled\n");
        return;
    }

    pci_save_state(dis_dev);
    pci_clear_master(dis_dev);
    pci_disable_device(dis_dev);
    do {
        struct acpi_device *ad = NULL;
        int r;

        r = acpi_bus_get_device(dis_handle, &ad);
        if (r || !ad) {
            pr_warn("Cannot get ACPI device for PCI device\n");
            break;
        }
        if (ad->power.state == ACPI_STATE_UNKNOWN) {
            pr_debug("ACPI power state is unknown, forcing D0\n");
            ad->power.state = ACPI_STATE_D0;
        }
    } while (0);
    pci_set_power_state(dis_dev, PCI_D3cold);

    if (bbswitch_acpi_off())
        pr_warn("The discrete card could not be disabled by a _DSM call\n");
}
Пример #10
0
static int __init bbswitch_init(void) {
    struct proc_dir_entry *acpi_entry;
    struct pci_dev *pdev = NULL;
    acpi_handle igd_handle = NULL;

    pr_info("version %s\n", BBSWITCH_VERSION);

    while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
        struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
        acpi_handle handle;
        int pci_class = pdev->class >> 8;

        if (pci_class != PCI_CLASS_DISPLAY_VGA &&
            pci_class != PCI_CLASS_DISPLAY_3D)
            continue;

        handle = DEVICE_ACPI_HANDLE(&pdev->dev);
        if (!handle) {
            pr_warn("cannot find ACPI handle for VGA device %s\n",
                dev_name(&pdev->dev));
            continue;
        }

        acpi_get_name(handle, ACPI_FULL_PATHNAME, &buf);

        if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
            igd_handle = handle;
            pr_info("Found integrated VGA device %s: %s\n",
                dev_name(&pdev->dev), (char *)buf.pointer);
        } else {
            dis_dev = pdev;
            dis_handle = handle;
            pr_info("Found discrete VGA device %s: %s\n",
                dev_name(&pdev->dev), (char *)buf.pointer);
        }
        kfree(buf.pointer);
    }

    if (dis_dev == NULL) {
        pr_err("No discrete VGA device found\n");
        return -ENODEV;
    }

    if (!skip_optimus_dsm &&
            has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) {
        dsm_type = DSM_TYPE_OPTIMUS;
        pr_info("detected an Optimus _DSM function\n");
    } else if (has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) {
        dsm_type = DSM_TYPE_NVIDIA;
        pr_info("detected a nVidia _DSM function\n");
    } else {
       /* At least two Acer machines are known to use the intel ACPI handle
        * with the legacy nvidia DSM */
        dis_handle = igd_handle;
        if (dis_handle && has_dsm_func(acpi_nvidia_dsm_muid, 0x102, 0x3)) {
            dsm_type = DSM_TYPE_NVIDIA;
            pr_info("detected a nVidia _DSM function on the"
                " integrated video card\n");
        } else {
            pr_err("No suitable _DSM call found.\n");
            return -ENODEV;
        }
    }

    acpi_entry = proc_create("bbswitch", 0664, acpi_root_dir, &bbswitch_fops);
    if (acpi_entry == NULL) {
        pr_err("Couldn't create proc entry\n");
        return -ENOMEM;
    }

    dis_dev_get();

    if (!is_card_disabled()) {
        /* We think the card is enabled, so ensure the kernel does as well */
        if (pci_enable_device(dis_dev))
            pr_warn("failed to enable %s\n", dev_name(&dis_dev->dev));
    }

    if (load_state == CARD_ON)
        bbswitch_on();
    else if (load_state == CARD_OFF)
        bbswitch_off();

    pr_info("Succesfully loaded. Discrete card %s is %s\n",
        dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");

    dis_dev_put();

    register_pm_notifier(&nb);

    return 0;
}
Пример #11
0
static int bbswitch_read(char *page, char **start, off_t off,
    int count, int *eof, void *data) {
    // show the card state. Example output: 0000:01:00:00 ON
    return snprintf(page, count, "%s %s\n", dev_name(&dis_dev->dev),
             is_card_disabled() ? "OFF" : "ON");
}