Skip to content

Commit

Permalink
Add nouveau switching method
Browse files Browse the repository at this point in the history
  • Loading branch information
abbradar committed Nov 21, 2016
1 parent a766409 commit 81e1200
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ bin_optirun_SOURCES = src/module.c src/bbconfig.c src/bblogger.c src/bbrun.c \
bin_optirun_LDADD = ${glib_LIBS} ${kmod_LIBS} -lrt
bin_bumblebeed_SOURCES = src/pci.c src/bbconfig.c src/bblogger.c src/bbrun.c \
src/bbsocket.c src/module.c src/bbsecondary.c src/switch/switching.c \
src/switch/sw_bbswitch.c src/switch/sw_switcheroo.c \
src/switch/sw_bbswitch.c src/switch/sw_switcheroo.c src/switch/sw_nouveau.c \
src/driver.c src/bumblebeed.c
bin_bumblebeed_LDADD = ${x11_LIBS} ${libbsd_LIBS} ${glib_LIBS} ${kmod_LIBS} -lrt

Expand Down
1 change: 1 addition & 0 deletions conf/bumblebee.conf.in
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ AllowFallbackToIGC=@CONF_FALLBACKSTART@
# detection resolves to NAME).
# PMMethod: method to use for saving power by disabling the nvidia card, valid
# values are: auto - automatically detect which PM method to use
# nouveau - use nouveau module with PM support
# bbswitch - new in BB 3, recommended if available
# switcheroo - vga_switcheroo method, use at your own risk
# none - disable PM completely
Expand Down
13 changes: 7 additions & 6 deletions src/bbconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const char *bb_pm_method_string[PM_METHODS_COUNT] = {
/* the below names are used in switch/switching.c */
"bbswitch",
"switcheroo",
"nouveau",
};

struct bb_status_struct bb_status;
Expand Down Expand Up @@ -183,13 +184,13 @@ void print_usage(int exit_val) {
-k, --driver-module NAME Name of kernel module to be loaded if different\n\
from the driver\n\
--pm-method METHOD method to use for disabling the discrete video card,\n\
valid values are auto, bbswitch, switcheroo and\n\
none. auto selects a sensible method,\n\
valid values are auto, bbswitch, nouveau,\n
switcheroo and none. auto selects a sensible method,\n\
bbswitch (kernel module) is available for nvidia\n\
and nouveau drivers,\n\
switcheroo (vga_switcheroo) is usually for\n\
nouveau and radeon drivers and none disables PM\n\
completely\n",
and nouveau drivers, nouveau uses this driver's PM\n\
faculities, switcheroo (vga_switcheroo) is usually for\n\
nouveau and radeon drivers and none disables PM\n\
completely\n",
out);
#ifdef WITH_PIDFILE
fputs("\
Expand Down
1 change: 1 addition & 0 deletions src/bbconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ enum bb_pm_method {
PM_AUTO, /* at detection time, this value will be changed */
PM_BBSWITCH,
PM_VGASWITCHEROO,
PM_NOUVEAU,
PM_METHODS_COUNT /* not a method but a marker for the end */
};
const char *bb_pm_method_string[PM_METHODS_COUNT];
Expand Down
165 changes: 165 additions & 0 deletions src/switch/sw_nouveau.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Copyright (c) 2011-2013, The Bumblebee Project
* Author: Jaron Viëtor AKA "Thulinma" <[email protected]>
* Author: Peter Lekensteyn <[email protected]>
*
* This file is part of Bumblebee.
*
* Bumblebee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Bumblebee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Bumblebee. If not, see <http://www.gnu.org/licenses/>.
*/

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/file.h>
#include "../bblogger.h"
#include "switching.h"
#include "../module.h"

static int nouveau_fd = -1;

/**
* Reports the status of nouveau
*
* @return SWITCH_OFF if nouveau is loaded (so card is power-managed),
* SWITCH_ON if card is on.
*/
enum switch_state nouveau_status(void) {
return nouveau_fd != -1 ? SWITCH_OFF : SWITCH_ON;
}//nouveau_status

/**
* Whether nouveau is available for use
*
* @param info A struct containing information which would help with the
* decision whether nouveau is usable or not
* @return 1 if available for use for PM, 0 otherwise
*/
int nouveau_is_available(struct switch_info info) {
if (strcmp(info.configured_pm, "nouveau") != 0) {
bb_log(LOG_INFO, "Skipping nouveau PM method because it is not"
" explicitly selected in the configuration.\n");
return 0;
}
return module_is_available("nouveau");
}

/**
* Unloads nouveau, leaving card unmanaged.
*/
void nouveau_on(void) {
/* Close nouveau device */
if (nouveau_fd != -1) {
close(nouveau_fd);
nouveau_fd = -1;
}

if (!module_unload("nouveau")) {
bb_log(LOG_ERR, "couldn't unload nouveau\n");
} else {
bb_log(LOG_DEBUG, "nouveau successfully unloaded\n");
}
}//nouveau_on

static const open_retry_delay_us = 5000;

/**
* Loads nouveau, hopefully powering card down.
*/
void nouveau_off(void) {
DIR *dp;
struct dirent *ep;
int card_no = -1;
char buf[PATH_MAX];
int open_retries = 10;

if (nouveau_fd != -1) {
return;
}

if (!module_load("nouveau", "nouveau", "runpm=1 modeset=2")) {
bb_log(LOG_WARNING, "couldn't load nouveau\n");
return;
}

/* Find card's device node by enumerating through existing cards. */
dp = opendir("/sys/class/drm");
if (dp == NULL) {
bb_log(LOG_WARNING, "couldn't open /sys/class/drm: %s\n", strerror(errno));
return;
}

while ((ep = readdir(dp))) {
int candidate_no;
char dummy;
// We use dummy to determine if name is "cardN" or "cardN-something" and skip the latter.
int read_count = sscanf(ep->d_name, "card%i%c", &candidate_no, &dummy);
if (read_count == 1) {
char* driver;
ssize_t link_size = 0;

snprintf(buf, sizeof(buf), "/sys/class/drm/card%i/device/driver", candidate_no);
link_size = readlink(buf, buf, sizeof(buf));
if (link_size < 0 || link_size == sizeof(buf)) {
bb_log(LOG_DEBUG, "couldn't read driver link for card %i\n", candidate_no);
continue;
}
buf[link_size] = '\0';
// We get a path like "../../../../bus/pci/drivers/driver_name" and want to test if driver is nouveau.
driver = strrchr(buf, '/');
if(driver == NULL) {
continue;
}
if(strcmp(driver + 1, "nouveau") == 0) {
card_no = candidate_no;
}
}
}

if(closedir(dp) != 0) {
bb_log(LOG_WARNING, "couldn't close /sys/class/drm: %s\n", strerror(errno));
}

if(card_no == -1) {
bb_log(LOG_WARNING, "couldn't find discrete card handled by nouveau\n");
return;
}

/* Open nouveau device and place exclusive lock to prevent Xorg from using it */
snprintf(buf, sizeof(buf), "/dev/dri/card%i", card_no);
bb_log(LOG_DEBUG, "found nouveau device: %s\n", buf);
/* udev needs time to create a device node */
for(; open_retries != 0; open_retries--) {
nouveau_fd = open(buf, 0);
if (nouveau_fd != -1) {
break;
} else {
usleep(open_retry_delay_us);
}
};

if (nouveau_fd == -1) {
bb_log(LOG_WARNING, "couldn't open nouveau device: %s\n", strerror(errno));
return;
}
if (flock(nouveau_fd, LOCK_EX) < 0) {
bb_log(LOG_WARNING, "couldn't exclusively lock nouveau device: %s\n", strerror(errno));
return;
}

bb_log(LOG_DEBUG, "successfully loaded and locked nouveau\n");
}//nouveau_off
2 changes: 2 additions & 0 deletions src/switch/switching.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

/* increase SWITCHERS_COUNT in switching.h when more methods are added */
struct switching_method switching_methods[SWITCHERS_COUNT] = {
{"nouveau", 2, nouveau_status, nouveau_is_available,
nouveau_on, nouveau_off},
{"bbswitch", 1, bbswitch_status, bbswitch_is_available,
bbswitch_on, bbswitch_off},
{"switcheroo", 0, switcheroo_status, switcheroo_is_available,
Expand Down
7 changes: 6 additions & 1 deletion src/switch/switching.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ struct switching_method {
void (*off)(void); /* attempts to disable a card */
};

enum switch_state nouveau_status(void);
int nouveau_is_available(struct switch_info);
void nouveau_on(void);
void nouveau_off(void);

enum switch_state bbswitch_status(void);
int bbswitch_is_available(struct switch_info);
void bbswitch_on(void);
Expand All @@ -59,7 +64,7 @@ void switcheroo_on(void);
void switcheroo_off(void);

/* number of switchers as defined in switching.c */
#define SWITCHERS_COUNT 2
#define SWITCHERS_COUNT 3
struct switching_method switching_methods[SWITCHERS_COUNT];

/* A switching method that can be used or NULL if none */
Expand Down

0 comments on commit 81e1200

Please sign in to comment.