udev-builtin-firmware.c revision 85eaf38c3b2fd70a8b01b72bbdb936c0a5944b3c
3488e51e244adfc756837287fbfbcc03eca8bf7avboxsync/*
b341a780162d809b187a8f35a10bba7642b69798vboxsync * firmware - Kernel firmware loader
b341a780162d809b187a8f35a10bba7642b69798vboxsync *
b341a780162d809b187a8f35a10bba7642b69798vboxsync * Copyright (C) 2009 Piter Punk <piterpunk@slackware.com>
b341a780162d809b187a8f35a10bba7642b69798vboxsync * Copyright (C) 2009-2011 Kay Sievers <kay.sievers@vrfy.org>
b341a780162d809b187a8f35a10bba7642b69798vboxsync *
b341a780162d809b187a8f35a10bba7642b69798vboxsync * This program is free software; you can redistribute it and/or
b341a780162d809b187a8f35a10bba7642b69798vboxsync * modify it under the terms of the GNU General Public License as
b341a780162d809b187a8f35a10bba7642b69798vboxsync * published by the Free Software Foundation; either version 2 of the
b341a780162d809b187a8f35a10bba7642b69798vboxsync * License, or (at your option) any later version.
b341a780162d809b187a8f35a10bba7642b69798vboxsync *
b341a780162d809b187a8f35a10bba7642b69798vboxsync * This program is distributed in the hope that it will be useful, but
b341a780162d809b187a8f35a10bba7642b69798vboxsync * WITHOUT ANY WARRANTY; without even the implied warranty of
b341a780162d809b187a8f35a10bba7642b69798vboxsync * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b341a780162d809b187a8f35a10bba7642b69798vboxsync * General Public License for more details:*
b341a780162d809b187a8f35a10bba7642b69798vboxsync */
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <unistd.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <stdlib.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <string.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <stdio.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <getopt.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <errno.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <stdbool.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <sys/utsname.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include <sys/stat.h>
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync#include "udev.h"
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsyncstatic bool set_loading(struct udev *udev, char *loadpath, const char *state)
b341a780162d809b187a8f35a10bba7642b69798vboxsync{
b341a780162d809b187a8f35a10bba7642b69798vboxsync FILE *ldfile;
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync ldfile = fopen(loadpath, "we");
c1888ea78e20e994aa8ec5780f6af6f5cec53438vboxsync if (ldfile == NULL) {
53545138e4a0872bfe432858276b85964d77af29vboxsync err(udev, "error: can not open '%s'\n", loadpath);
99ea85d57b86febcbb71ed923c8e628ef9406105vboxsync return false;
b5c691a79b5dca21a249bd50405cc42c0572985dvboxsync };
085bc29163eb87e345acaae02789e4c233d51f3bvboxsync fprintf(ldfile, "%s\n", state);
b341a780162d809b187a8f35a10bba7642b69798vboxsync fclose(ldfile);
b341a780162d809b187a8f35a10bba7642b69798vboxsync return true;
b341a780162d809b187a8f35a10bba7642b69798vboxsync}
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsyncstatic bool copy_firmware(struct udev *udev, const char *source, const char *target, size_t size)
1910146bc46e3eee5b8668806da594107fe9aabfvboxsync{
a4b605335b7e6a6ba6d5a301bc96de0e89e4b663vboxsync char *buf;
b341a780162d809b187a8f35a10bba7642b69798vboxsync FILE *fsource = NULL, *ftarget = NULL;
b341a780162d809b187a8f35a10bba7642b69798vboxsync bool ret = false;
b341a780162d809b187a8f35a10bba7642b69798vboxsync
ca04977659aed9435519a9aa8fdda2caba315fabvboxsync buf = malloc(size);
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (buf == NULL) {
b341a780162d809b187a8f35a10bba7642b69798vboxsync err(udev,"No memory available to load firmware file");
b341a780162d809b187a8f35a10bba7642b69798vboxsync return false;
b341a780162d809b187a8f35a10bba7642b69798vboxsync }
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync info(udev, "writing '%s' (%zi) to '%s'\n", source, size, target);
b341a780162d809b187a8f35a10bba7642b69798vboxsync
ca04977659aed9435519a9aa8fdda2caba315fabvboxsync fsource = fopen(source, "re");
ca04977659aed9435519a9aa8fdda2caba315fabvboxsync if (fsource == NULL)
ca04977659aed9435519a9aa8fdda2caba315fabvboxsync goto exit;
b341a780162d809b187a8f35a10bba7642b69798vboxsync ftarget = fopen(target, "we");
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (ftarget == NULL)
b341a780162d809b187a8f35a10bba7642b69798vboxsync goto exit;
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (fread(buf, size, 1, fsource) != 1)
b341a780162d809b187a8f35a10bba7642b69798vboxsync goto exit;
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (fwrite(buf, size, 1, ftarget) == 1)
b341a780162d809b187a8f35a10bba7642b69798vboxsync ret = true;
b341a780162d809b187a8f35a10bba7642b69798vboxsyncexit:
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (ftarget != NULL)
b341a780162d809b187a8f35a10bba7642b69798vboxsync fclose(ftarget);
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (fsource != NULL)
b341a780162d809b187a8f35a10bba7642b69798vboxsync fclose(fsource);
b341a780162d809b187a8f35a10bba7642b69798vboxsync free(buf);
b341a780162d809b187a8f35a10bba7642b69798vboxsync return ret;
b341a780162d809b187a8f35a10bba7642b69798vboxsync}
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsyncstatic int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test)
b341a780162d809b187a8f35a10bba7642b69798vboxsync{
ca04977659aed9435519a9aa8fdda2caba315fabvboxsync struct udev *udev = udev_device_get_udev(dev);
b341a780162d809b187a8f35a10bba7642b69798vboxsync static const char *searchpath[] = { FIRMWARE_PATH };
ca04977659aed9435519a9aa8fdda2caba315fabvboxsync char fwencpath[UTIL_PATH_SIZE];
b341a780162d809b187a8f35a10bba7642b69798vboxsync char misspath[UTIL_PATH_SIZE];
b341a780162d809b187a8f35a10bba7642b69798vboxsync char loadpath[UTIL_PATH_SIZE];
f7422473f089cb7f3b8be1c3fc223c441627d15cvboxsync char datapath[UTIL_PATH_SIZE];
b341a780162d809b187a8f35a10bba7642b69798vboxsync char fwpath[UTIL_PATH_SIZE];
f7422473f089cb7f3b8be1c3fc223c441627d15cvboxsync const char *firmware;
b341a780162d809b187a8f35a10bba7642b69798vboxsync FILE *fwfile;
b341a780162d809b187a8f35a10bba7642b69798vboxsync struct utsname kernel;
b341a780162d809b187a8f35a10bba7642b69798vboxsync struct stat statbuf;
b341a780162d809b187a8f35a10bba7642b69798vboxsync unsigned int i;
b341a780162d809b187a8f35a10bba7642b69798vboxsync int rc = EXIT_SUCCESS;
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync firmware = udev_device_get_property_value(dev, "FIRMWARE");
9f801b83895937ce4d726206e5859637ff9f5166vboxsync if (firmware == NULL) {
b341a780162d809b187a8f35a10bba7642b69798vboxsync err(udev, "firmware parameter missing\n\n");
b341a780162d809b187a8f35a10bba7642b69798vboxsync rc = EXIT_FAILURE;
b341a780162d809b187a8f35a10bba7642b69798vboxsync goto exit;
b341a780162d809b187a8f35a10bba7642b69798vboxsync }
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync /* lookup firmware file */
b341a780162d809b187a8f35a10bba7642b69798vboxsync uname(&kernel);
b341a780162d809b187a8f35a10bba7642b69798vboxsync for (i = 0; i < ARRAY_SIZE(searchpath); i++) {
b341a780162d809b187a8f35a10bba7642b69798vboxsync util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL);
b341a780162d809b187a8f35a10bba7642b69798vboxsync dbg(udev, "trying %s\n", fwpath);
b341a780162d809b187a8f35a10bba7642b69798vboxsync fwfile = fopen(fwpath, "re");
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (fwfile != NULL)
374979da4e9440f386798bce28aa9165f3fb5e3avboxsync break;
374979da4e9440f386798bce28aa9165f3fb5e3avboxsync
374979da4e9440f386798bce28aa9165f3fb5e3avboxsync util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL);
b341a780162d809b187a8f35a10bba7642b69798vboxsync dbg(udev, "trying %s\n", fwpath);
b341a780162d809b187a8f35a10bba7642b69798vboxsync fwfile = fopen(fwpath, "re");
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (fwfile != NULL)
b341a780162d809b187a8f35a10bba7642b69798vboxsync break;
b341a780162d809b187a8f35a10bba7642b69798vboxsync }
cae5cca5168e18e168df5541b11f462b60062a7avboxsync
cae5cca5168e18e168df5541b11f462b60062a7avboxsync util_path_encode(firmware, fwencpath, sizeof(fwencpath));
b341a780162d809b187a8f35a10bba7642b69798vboxsync util_strscpyl(misspath, sizeof(misspath), udev_get_run_path(udev), "/firmware-missing/", fwencpath, NULL);
b341a780162d809b187a8f35a10bba7642b69798vboxsync util_strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL);
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (fwfile == NULL) {
b341a780162d809b187a8f35a10bba7642b69798vboxsync int err;
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync /* This link indicates the missing firmware file and the associated device */
b341a780162d809b187a8f35a10bba7642b69798vboxsync info(udev, "did not find firmware file '%s'\n", firmware);
b341a780162d809b187a8f35a10bba7642b69798vboxsync do {
b341a780162d809b187a8f35a10bba7642b69798vboxsync err = util_create_path(udev, misspath);
cae5cca5168e18e168df5541b11f462b60062a7avboxsync if (err != 0 && err != -ENOENT)
b341a780162d809b187a8f35a10bba7642b69798vboxsync break;
b341a780162d809b187a8f35a10bba7642b69798vboxsync err = symlink(udev_device_get_devpath(dev), misspath);
1910146bc46e3eee5b8668806da594107fe9aabfvboxsync if (err != 0)
1910146bc46e3eee5b8668806da594107fe9aabfvboxsync err = -errno;
b341a780162d809b187a8f35a10bba7642b69798vboxsync } while (err == -ENOENT);
1910146bc46e3eee5b8668806da594107fe9aabfvboxsync rc = EXIT_FAILURE;
b341a780162d809b187a8f35a10bba7642b69798vboxsync set_loading(udev, loadpath, "-1");
cae5cca5168e18e168df5541b11f462b60062a7avboxsync goto exit;
b341a780162d809b187a8f35a10bba7642b69798vboxsync }
b341a780162d809b187a8f35a10bba7642b69798vboxsync
a4b605335b7e6a6ba6d5a301bc96de0e89e4b663vboxsync if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) {
a4b605335b7e6a6ba6d5a301bc96de0e89e4b663vboxsync rc = EXIT_FAILURE;
b341a780162d809b187a8f35a10bba7642b69798vboxsync goto exit;
a4b605335b7e6a6ba6d5a301bc96de0e89e4b663vboxsync }
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (unlink(misspath) == 0)
a4b605335b7e6a6ba6d5a301bc96de0e89e4b663vboxsync util_delete_path(udev, misspath);
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (!set_loading(udev, loadpath, "1"))
b341a780162d809b187a8f35a10bba7642b69798vboxsync goto exit;
b341a780162d809b187a8f35a10bba7642b69798vboxsync
b341a780162d809b187a8f35a10bba7642b69798vboxsync util_strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL);
b341a780162d809b187a8f35a10bba7642b69798vboxsync if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) {
b341a780162d809b187a8f35a10bba7642b69798vboxsync err(udev, "error sending firmware '%s' to device\n", firmware);
8b048c9203f6823d1a06b0235c7c3cb633c6e3a5vboxsync set_loading(udev, loadpath, "-1");
8b048c9203f6823d1a06b0235c7c3cb633c6e3a5vboxsync rc = EXIT_FAILURE;
8b048c9203f6823d1a06b0235c7c3cb633c6e3a5vboxsync goto exit;
8b048c9203f6823d1a06b0235c7c3cb633c6e3a5vboxsync };
8b048c9203f6823d1a06b0235c7c3cb633c6e3a5vboxsync
9f801b83895937ce4d726206e5859637ff9f5166vboxsync set_loading(udev, loadpath, "0");
9f801b83895937ce4d726206e5859637ff9f5166vboxsyncexit:
9f801b83895937ce4d726206e5859637ff9f5166vboxsync if (fwfile)
9f801b83895937ce4d726206e5859637ff9f5166vboxsync fclose(fwfile);
9f801b83895937ce4d726206e5859637ff9f5166vboxsync return rc;
9f801b83895937ce4d726206e5859637ff9f5166vboxsync}
9f801b83895937ce4d726206e5859637ff9f5166vboxsync
9f801b83895937ce4d726206e5859637ff9f5166vboxsyncconst struct udev_builtin udev_builtin_firmware = {
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync .name = "firmware",
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync .cmd = builtin_firmware,
0de1998ac52682bb5322df476e45f237265ea9b7vboxsync .help = "kernel firmware loader",
9f801b83895937ce4d726206e5859637ff9f5166vboxsync .run_once = true,
9f801b83895937ce4d726206e5859637ff9f5166vboxsync};
770da3dbb247278c98d1b21d2e11a0a7769131a4vboxsync