static bool ply_renderer_head_add_connector (ply_renderer_head_t *head, drmModeConnector *connector, int connector_mode_index) { drmModeModeInfo *mode; mode = &connector->modes[connector_mode_index]; if (mode->hdisplay != head->area.width || mode->vdisplay != head->area.height) { ply_trace ("Tried to add connector with resolution %dx%d to %dx%d head", (int) mode->hdisplay, (int) mode->vdisplay, (int) head->area.width, (int) head->area.height); return false; } else { ply_trace ("Adding connector with id %d to %dx%d head", (int) connector->connector_id, (int) head->area.width, (int) head->area.height); } ply_array_add_uint32_element (head->connector_ids, connector->connector_id); return true; }
static bool fetch_buffer (ply_renderer_driver_t *driver, uint32_t buffer_id, unsigned long *width, unsigned long *height, unsigned long *row_stride) { ply_renderer_buffer_t *buffer; buffer = get_buffer_from_id (driver, buffer_id); if (buffer == NULL) { ply_trace ("could not fetch buffer %u", buffer_id); return false; } if (width != NULL) *width = buffer->width; if (height != NULL) *height = buffer->height; if (row_stride != NULL) *row_stride = buffer->row_stride; ply_trace ("fetched %ux%u buffer with stride %u", buffer->width, buffer->height, buffer->row_stride); return true; }
static bool query_device (ply_renderer_backend_t *backend) { assert (backend != NULL); assert (backend->device_fd >= 0); backend->resources = drmModeGetResources (backend->device_fd); if (backend->resources == NULL) { ply_trace ("Could not get card resources"); return false; } if (!create_heads_for_active_connectors (backend)) { ply_trace ("Could not initialize heads"); return false; } if (!has_32bpp_support (backend)) { ply_trace ("Device doesn't support 32bpp framebuffer"); return false; } return true; }
static uint32_t create_buffer (ply_renderer_driver_t *driver, unsigned long width, unsigned long height, unsigned long *row_stride) { ply_renderer_buffer_t *buffer; buffer = ply_renderer_buffer_new (driver, width, height); if (buffer == NULL) { ply_trace ("Could not allocate GEM object for frame buffer: %m"); return 0; } if (drmModeAddFB (driver->device_fd, width, height, 24, 32, buffer->row_stride, buffer->handle, &buffer->id) != 0) { ply_trace ("Could not set up GEM object as frame buffer: %m"); ply_renderer_buffer_free (driver, buffer); return 0; } *row_stride = buffer->row_stride; buffer->added_fb = true; ply_hashtable_insert (driver->buffers, (void *) (uintptr_t) buffer->id, buffer); return buffer->id; }
static bool show_splash_screen (ply_boot_splash_plugin_t *plugin, ply_event_loop_t *loop, ply_buffer_t *boot_buffer, ply_boot_splash_mode_t mode) { assert (plugin != NULL); if (ply_list_get_length (plugin->displays) == 0) { ply_trace ("no pixel displays"); return false; } plugin->loop = loop; plugin->mode = mode; ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t) detach_from_event_loop, plugin); ply_event_loop_watch_signal (plugin->loop, SIGINT, (ply_event_handler_t) on_interrupt, plugin); ply_trace ("starting boot animation"); return start_animation (plugin); }
static void ply_terminal_reopen_device (ply_terminal_t *terminal) { ply_terminal_open_result_t open_result; ply_trace ("trying to reopen terminal '%s' (attempt %d)", terminal->name, terminal->number_of_reopen_tries); terminal->number_of_reopen_tries++; open_result = ply_terminal_open_device (terminal); if (open_result == PLY_TERMINAL_OPEN_RESULT_INCOMPLETE) { int total_retries; total_retries = (int) (PLY_TERMINAL_REOPEN_TIMEOUT / PLY_TERMINAL_REOPEN_INTERVAL); if (terminal->number_of_reopen_tries < total_retries) { ply_event_loop_watch_for_timeout (terminal->loop, PLY_TERMINAL_REOPEN_INTERVAL, (ply_event_loop_timeout_handler_t) ply_terminal_reopen_device, terminal); } else { ply_trace ("couldn't reopen tty, giving up"); terminal->number_of_reopen_tries = 0; } } }
static bool get_kernel_command_line (state_t *state) { int fd; ply_trace ("opening /proc/cmdline"); fd = open ("proc/cmdline", O_RDONLY); if (fd < 0) { ply_trace ("couldn't open it: %m"); return false; } ply_trace ("reading kernel command line"); if (read (fd, state->kernel_command_line, sizeof (state->kernel_command_line)) < 0) { ply_trace ("couldn't read it: %m"); return false; } ply_trace ("Kernel command line is: '%s'", state->kernel_command_line); close (fd); return true; }
void ply_terminal_close (ply_terminal_t *terminal) { if (!terminal->is_open) { ply_trace ("terminal %s is already closed", terminal->name); return; } terminal->is_open = false; ply_terminal_stop_watching_for_vt_changes (terminal); ply_trace ("restoring color palette"); ply_terminal_restore_color_palette (terminal); if (terminal->fd_watch != NULL) { ply_trace ("stop watching tty fd"); ply_event_loop_stop_watching_fd (terminal->loop, terminal->fd_watch); terminal->fd_watch = NULL; } if (terminal->loop != NULL) { ply_trace ("stop watching SIGWINCH signal"); ply_event_loop_stop_watching_signal (terminal->loop, SIGWINCH); } ply_trace ("setting buffered input"); ply_terminal_set_buffered_input (terminal); close (terminal->fd); terminal->fd = -1; }
static bool ply_renderer_head_set_scan_out_buffer (ply_renderer_backend_t *backend, ply_renderer_head_t *head, uint32_t buffer_id) { drmModeModeInfo *mode; uint32_t *connector_ids; int number_of_connectors; connector_ids = (uint32_t *) ply_array_get_uint32_elements (head->connector_ids); number_of_connectors = ply_array_get_size (head->connector_ids); mode = &head->connector0->modes[head->connector0_mode_index]; ply_trace ("Setting scan out buffer of %ldx%ld head to our buffer", head->area.width, head->area.height); /* Tell the controller to use the allocated scan out buffer on each connectors */ if (drmModeSetCrtc (backend->device_fd, head->controller_id, buffer_id, 0, 0, connector_ids, number_of_connectors, mode) < 0) { ply_trace ("Couldn't set scan out buffer for head with controller id %d", head->controller_id); return false; } return true; }
static bool open_device (ply_renderer_backend_t *backend) { assert (backend != NULL); assert (backend->device_name != NULL); if (!load_driver (backend)) return false; if (!ply_terminal_open (backend->terminal)) { ply_trace ("could not open terminal: %m"); return false; } if (!ply_terminal_is_vt (backend->terminal)) { ply_trace ("terminal is not a VT"); ply_terminal_close (backend->terminal); return false; } ply_terminal_watch_for_active_vt_change (backend->terminal, (ply_terminal_active_vt_changed_handler_t) on_active_vt_changed, backend); return true; }
static bool answer_via_command (char *command, const char *answer, int *exit_status) { bool gave_answer; pid_t pid; int command_input_sender_fd, command_input_receiver_fd; ply_trace ("running command '%s'", command); /* answer may be NULL which means, * "The daemon can't ask the user questions, * do all the prompting from the client" */ gave_answer = false; if (answer != NULL && !ply_open_unidirectional_pipe (&command_input_sender_fd, &command_input_receiver_fd)) return false; pid = fork (); if (pid < 0) return false; if (pid == 0) { char **args; args = split_string (command, ' '); if (answer != NULL) { close (command_input_sender_fd); dup2 (command_input_receiver_fd, STDIN_FILENO); } execvp (args[0], args); ply_trace ("could not run command: %m"); _exit (127); } if (answer != NULL) { close (command_input_receiver_fd); if (write (command_input_sender_fd, answer, strlen (answer)) < 0) goto out; } gave_answer = true; out: if (answer != NULL) close (command_input_sender_fd); waitpid (pid, exit_status, 0); return gave_answer; }
static bool ply_renderer_load_plugin (ply_renderer_t *renderer, const char *module_path) { assert (renderer != NULL); get_backend_interface_function_t get_renderer_backend_interface; renderer->module_handle = ply_open_module (module_path); if (renderer->module_handle == NULL) return false; get_renderer_backend_interface = (get_backend_interface_function_t) ply_module_look_up_function (renderer->module_handle, "ply_renderer_backend_get_interface"); if (get_renderer_backend_interface == NULL) { ply_save_errno (); ply_trace ("module '%s' is not a renderer plugin", module_path); ply_close_module (renderer->module_handle); renderer->module_handle = NULL; ply_restore_errno (); return false; } renderer->plugin_interface = get_renderer_backend_interface (); if (renderer->plugin_interface == NULL) { ply_trace ("module '%s' is not a valid renderer plugin", module_path); ply_save_errno (); ply_close_module (renderer->module_handle); renderer->module_handle = NULL; ply_restore_errno (); return false; } renderer->backend = renderer->plugin_interface->create_backend (renderer->device_name, renderer->terminal); if (renderer->backend == NULL) { ply_save_errno (); ply_trace ("module '%s' renderer backend could not be created", module_path); ply_close_module (renderer->module_handle); renderer->module_handle = NULL; ply_restore_errno (); return false; } return true; }
static bool show_splash_screen (ply_boot_splash_plugin_t *plugin, ply_event_loop_t *loop, ply_buffer_t *boot_buffer, ply_boot_splash_mode_t mode) { assert (plugin != NULL); plugin->loop = loop; plugin->mode = mode; ply_trace ("loading lock image"); if (!ply_image_load (plugin->lock_image)) return false; ply_trace ("loading box image"); if (!ply_image_load (plugin->box_image)) return false; if (plugin->corner_image != NULL) { ply_trace ("loading corner image"); if (!ply_image_load (plugin->corner_image)) { ply_image_free (plugin->corner_image); plugin->corner_image = NULL; } } if (!load_views (plugin)) { ply_trace ("couldn't load views"); return false; } ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t) detach_from_event_loop, plugin); ply_event_loop_watch_signal (plugin->loop, SIGINT, (ply_event_handler_t) on_interrupt, plugin); ply_trace ("starting boot animation"); start_progress_animation (plugin); plugin->is_visible = true; return true; }
static bool load_driver (ply_renderer_backend_t *backend) { char *driver_name; int device_fd; driver_name = find_driver_for_device (backend->device_name); ply_trace ("Attempting to load driver '%s'", driver_name); device_fd = drmOpen (driver_name, NULL); if (device_fd < 0) { ply_trace ("drmOpen failed"); free (driver_name); return false; } if (strcmp (driver_name, "i915") == 0) { backend->driver_interface = ply_renderer_i915_driver_get_interface (); backend->driver_supports_mapping_console = true; } else if (strcmp (driver_name, "radeon") == 0) { backend->driver_interface = ply_renderer_radeon_driver_get_interface (); backend->driver_supports_mapping_console = false; } else if (strcmp (driver_name, "nouveau") == 0) { backend->driver_interface = ply_renderer_nouveau_driver_get_interface (); backend->driver_supports_mapping_console = false; } free (driver_name); if (backend->driver_interface == NULL) { close (device_fd); return false; } backend->driver = backend->driver_interface->create_driver (device_fd); if (backend->driver == NULL) { close (device_fd); return false; } backend->device_fd = device_fd; return true; }
static bool view_load (view_t *view) { ply_trace ("loading entry"); if (!ply_entry_load (view->entry)) return false; ply_trace ("loading throbber"); if (!ply_throbber_load (view->throbber)) return false; return true; }
static void on_password_answer_failure (password_answer_state_t *answer_state, ply_boot_client_t *client) { ply_trace ("password answer failure"); /* plymouthd isn't running for some reason. If there is a command * to run, we'll run it anyway, because it might be important for * boot up to continue (to decrypt the root partition or whatever) */ if (answer_state->command != NULL) { int exit_status; bool command_started; ply_trace ("daemon not available, running command on our own"); exit_status = 127; command_started = false; while (answer_state->number_of_tries_left > 0) { command_started = answer_via_command (answer_state->command, NULL, &exit_status); if (command_started && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0) { ply_trace ("command was successful"); break; } ply_trace ("command failed"); answer_state->number_of_tries_left--; } if (command_started && WIFSIGNALED (exit_status)) { ply_trace ("command died with signal %s", strsignal (WTERMSIG (exit_status))); raise (WTERMSIG (exit_status)); } else { ply_event_loop_exit (answer_state->state->loop, WEXITSTATUS (exit_status)); } } else { ply_event_loop_exit (answer_state->state->loop, 1); } }
static void become_idle (ply_boot_splash_plugin_t *plugin, ply_trigger_t *idle_trigger) { ply_trace ("deactivation requested"); if (plugin->is_idle) { ply_trace ("plugin is already idle"); ply_trigger_pull (idle_trigger, NULL); return; } stop_animation (plugin, idle_trigger); plugin->is_idle = true; }
static void on_active_vt_changed (ply_renderer_backend_t *backend) { if (ply_terminal_is_active (backend->terminal)) { ply_trace ("activating on vt change"); activate (backend); } else { ply_trace ("deactivating on vt change"); deactivate (backend); } }
static void on_multiple_password_answers (password_answer_state_t *answer_state, const char * const *answers) { bool need_to_ask_user; int i; int exit_status; assert (answer_state->command != NULL); ply_trace ("on multiple password answers"); need_to_ask_user = true; if (answers != NULL) { ply_trace ("daemon has a few candidate passwords for us to try"); for (i = 0; answers[i] != NULL; i++) { bool command_started; exit_status = 127; command_started = answer_via_command (answer_state->command, answers[i], &exit_status); if (command_started && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0) { need_to_ask_user = false; break; } } } else { ply_trace ("daemon has no candidate passwords for us to try"); } if (need_to_ask_user) { ply_boot_client_ask_daemon_for_password (answer_state->state->client, answer_state->prompt, (ply_boot_client_answer_handler_t) on_password_answer, (ply_boot_client_response_handler_t) on_password_answer_failure, answer_state); return; } ply_event_loop_exit (answer_state->state->loop, 0); }
bool ply_renderer_open (ply_renderer_t *renderer) { int i; /* FIXME: at some point we may want to make this * part more dynamic (so you don't have to edit this * list to add a new renderer) */ const char *known_plugins[] = { PLYMOUTH_PLUGIN_PATH "renderers/x11.so", PLYMOUTH_PLUGIN_PATH "renderers/drm.so", PLYMOUTH_PLUGIN_PATH "renderers/frame-buffer.so", NULL }; for (i = 0; known_plugins[i] != NULL; i++) { const char *plugin_path; plugin_path = known_plugins[i]; if (!ply_renderer_load_plugin (renderer, plugin_path)) continue; if (!ply_renderer_open_device (renderer)) { ply_trace ("could not open rendering device for plugin %s", plugin_path); ply_renderer_unload_plugin (renderer); continue; } if (!ply_renderer_query_device (renderer)) { ply_trace ("could not query rendering device for plugin %s", plugin_path); ply_renderer_close_device (renderer); ply_renderer_unload_plugin (renderer); continue; } return true; } ply_trace ("could not find suitable rendering plugin"); return false; }
static bool ply_renderer_head_map (ply_renderer_backend_t *backend, ply_renderer_head_t *head) { bool scan_out_set; assert (backend != NULL); assert (backend->device_fd >= 0); assert (backend->driver_interface != NULL); assert (backend->driver != NULL); assert (head != NULL); ply_trace ("Creating buffer for %ldx%ld renderer head", head->area.width, head->area.height); head->scan_out_buffer_id = backend->driver_interface->create_buffer (backend->driver, head->area.width, head->area.height, &head->row_stride); if (head->scan_out_buffer_id == 0) return false; ply_trace ("Mapping buffer for %ldx%ld renderer head", head->area.width, head->area.height); if (!backend->driver_interface->map_buffer (backend->driver, head->scan_out_buffer_id)) { backend->driver_interface->destroy_buffer (backend->driver, head->scan_out_buffer_id); head->scan_out_buffer_id = 0; return false; } /* FIXME: Maybe we should blit the fbcon contents instead of the (blank) * shadow buffer? */ ply_renderer_head_redraw (backend, head); scan_out_set = reset_scan_out_buffer_if_needed (backend, head); if (!scan_out_set && backend->is_active) { backend->driver_interface->destroy_buffer (backend->driver, head->scan_out_buffer_id); head->scan_out_buffer_id = 0; return false; } return true; }
static void ply_boot_connection_on_keystroke_answer (ply_boot_connection_t *connection, const char *key) { ply_trace ("got key: %s", key); ply_boot_connection_send_answer (connection, key); }
static void ply_boot_connection_on_question_answer (ply_boot_connection_t *connection, const char *answer) { ply_trace ("got question answer: %s", answer); ply_boot_connection_send_answer (connection, answer); }
static void remove_pixel_display (ply_boot_splash_plugin_t *plugin, ply_pixel_display_t *display) { ply_list_node_t *node; ply_trace ("removing pixel display from plugin"); node = ply_list_get_first_node (plugin->views); while (node != NULL) { view_t *view; ply_list_node_t *next_node; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); if (view->display == display) { ply_pixel_display_set_draw_handler (view->display, NULL, NULL); view_free (view); ply_list_remove_node (plugin->views, node); return; } node = next_node; } }
static void start_animation (ply_boot_splash_plugin_t *plugin) { ply_list_node_t *node; if (plugin->is_animating) return; ply_trace ("starting animation"); node = ply_list_get_first_node (plugin->views); while (node != NULL) { ply_list_node_t *next_node; view_t *view; view = ply_list_node_get_data (node); next_node = ply_list_get_next_node (plugin->views, node); view_start_animation (view); node = next_node; } plugin->is_animating = true; if (plugin->mode == PLY_BOOT_SPLASH_MODE_SHUTDOWN) plugin->is_idle = true; }
static bool ply_renderer_buffer_map (ply_renderer_driver_t *driver, ply_renderer_buffer_t *buffer) { struct drm_mode_map_dumb map_dumb_buffer_request; void *map_address; if (buffer->map_address != MAP_FAILED) { buffer->map_count++; return true; } memset (&map_dumb_buffer_request, 0, sizeof(struct drm_mode_map_dumb)); map_dumb_buffer_request.handle = buffer->handle; if (drmIoctl (driver->device_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb_buffer_request) < 0) { ply_trace ("Could not map GEM object %u: %m", buffer->handle); return false; } map_address = mmap (0, buffer->map_size, PROT_READ | PROT_WRITE, MAP_SHARED, driver->device_fd, map_dumb_buffer_request.offset); if (map_address == MAP_FAILED) return false; buffer->map_address = map_address; buffer->map_count++; return true; }
static void activate (ply_renderer_backend_t *backend) { ply_list_node_t *node; ply_trace ("taking master and scanning out"); backend->is_active = true; drmSetMaster (backend->device_fd); node = ply_list_get_first_node (backend->heads); while (node != NULL) { ply_list_node_t *next_node; ply_renderer_head_t *head; head = (ply_renderer_head_t *) ply_list_node_get_data (node); next_node = ply_list_get_next_node (backend->heads, node); if (head->scan_out_buffer_id != 0) { /* Flush out any pending drawing to the buffer */ flush_head (backend, head); /* Then send the buffer to the monitor */ ply_renderer_head_set_scan_out_buffer (backend, head, head->scan_out_buffer_id); } node = next_node; } }
static void deactivate (ply_renderer_backend_t *backend) { ply_trace ("dropping master"); drmDropMaster (backend->device_fd); backend->is_active = false; }
static void detach_from_event_loop (ply_boot_splash_plugin_t *plugin) { plugin->loop = NULL; ply_trace ("detaching from event loop"); }
static int get_index_of_active_mode (ply_renderer_backend_t *backend, drmModeCrtc *controller, drmModeConnector *connector) { if (!controller->mode_valid) { ply_trace ("No valid mode currently active on monitor"); return -1; } ply_trace ("Looking for connector mode index of active mode %dx%d", controller->mode.hdisplay, controller->mode.vdisplay); return find_index_of_mode (backend, connector, &controller->mode); }