firmware.c revision 5f59fa0900a5c127ce1a25d8ba3176f166662f96
d657c51f14601d0235434ffb78cf6ac0f27cc83cLennart Poettering/*
220a21d38f675eb835f5758e3d23e896573aa5eaLennart Poettering * firmware - Load firmware device
b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12Kay Sievers *
b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12Kay Sievers * Copyright (C) 2009 Piter Punk <piterpunk@slackware.com>
b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12Kay Sievers * Copyright (C) 2009 Kay Sievers <kay.sievers@vrfy.org>
b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12Kay Sievers *
b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12Kay Sievers * This program is free software; you can redistribute it and/or
b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12Kay Sievers * modify it under the terms of the GNU General Public License as
b72ddf0f4f552dd53d6404b6ddbc9f17d02b8e12Kay Sievers * published by the Free Software Foundation; either version 2 of the
3dff3e00e044e2d53c76fa842b9a4759d4a50e69Kay Sievers * License, or (at your option) any later version.
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering *
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering * This program is distributed in the hope that it will be useful, but
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering * WITHOUT ANY WARRANTY; without even the implied warranty of
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering * General Public License for more details:*
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering */
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <unistd.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <stdlib.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <string.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <stdio.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <getopt.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <errno.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <stdbool.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <sys/utsname.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include <sys/stat.h>
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering#include "libudev-private.h"
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poetteringstatic bool set_loading(struct udev *udev, char *loadpath, const char *state)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering{
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering FILE *ldfile;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering ldfile = fopen(loadpath, "w");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (ldfile == NULL) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering err(udev, "error: can not open '%s'\n", loadpath);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering return false;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering };
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering fprintf(ldfile, "%s\n", state);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering fclose(ldfile);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering return true;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering}
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poetteringstatic bool copy_firmware(struct udev *udev, const char *source, const char *target, size_t size)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering{
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char *buf;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering FILE *fsource, *ftarget;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering bool ret = false;
ce1dde29b92d1399ce502e0f7db790a99d14841fThomas Hindoe Paaboel Andersen
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering buf = malloc(size);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (buf == NULL) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering err(udev,"No memory available to load firmware file");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering return false;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering }
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering fsource = fopen(source, "r");
3dff3e00e044e2d53c76fa842b9a4759d4a50e69Kay Sievers if (fsource == NULL)
3dff3e00e044e2d53c76fa842b9a4759d4a50e69Kay Sievers goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering ftarget = fopen(target, "w");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (ftarget == NULL)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (fread(buf, size, 1, fsource) != 1)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (fwrite(buf, size, 1, ftarget) == 1)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering ret = true;
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poetteringexit:
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poettering if (ftarget != NULL)
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poettering fclose(ftarget);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (fsource != NULL)
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poettering fclose(fsource);
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poettering free(buf);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering return ret;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering}
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poetteringint main(int argc, char **argv)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering{
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering static const struct option options[] = {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering { "firmware", required_argument, NULL, 'f' },
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering { "devpath", required_argument, NULL, 'p' },
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering { "help", no_argument, NULL, 'h' },
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering {}
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering };
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering static const char *searchpath[] = { FIRMWARE_PATH };
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char fwencpath[UTIL_PATH_SIZE];
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char misspath[UTIL_PATH_SIZE];
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char loadpath[UTIL_PATH_SIZE];
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char datapath[UTIL_PATH_SIZE];
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char fwpath[UTIL_PATH_SIZE];
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char *devpath = NULL;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering char *firmware = NULL;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering FILE *fwfile;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering struct utsname kernel;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering struct stat statbuf;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering struct udev *udev = NULL;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering unsigned int i;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering int rc = 0;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering udev_log_init("firmware");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering for (;;) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering int option;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering option = getopt_long(argc, argv, "f:p:h", options, NULL);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (option == -1)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering break;
ce1dde29b92d1399ce502e0f7db790a99d14841fThomas Hindoe Paaboel Andersen
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering switch (option) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering case 'f':
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering firmware = optarg;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering break;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering case 'p':
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering devpath = optarg;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering break;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering case 'h':
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering printf("Usage: firmware --firmware=<fwfile> --devpath=<path> [--help]\n\n");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering default:
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering rc = 1;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering }
ce1dde29b92d1399ce502e0f7db790a99d14841fThomas Hindoe Paaboel Andersen }
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (devpath == NULL || firmware == NULL) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering fprintf(stderr, "firmware or devpath parameter missing\n\n");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering rc = 1;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering }
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering udev = udev_new();
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (udev == NULL) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering rc = 1;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering };
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering /* lookup firmware file */
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering uname(&kernel);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering for (i = 0; i < ARRAY_SIZE(searchpath); i++) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering dbg(udev, "trying %s\n", fwpath);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering fwfile = fopen(fwpath, "r");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (fwfile != NULL)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering break;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering dbg(udev, "trying %s\n", fwpath);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering fwfile = fopen(fwpath, "r");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (fwfile != NULL)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering break;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering }
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering util_path_encode(firmware, fwencpath, sizeof(fwencpath));
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering util_strscpyl(misspath, sizeof(misspath), udev_get_dev_path(udev), "/.run/udev/firmware-missing/", fwencpath, NULL);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering util_strscpyl(loadpath, sizeof(loadpath), udev_get_sys_path(udev), devpath, "/loading", NULL);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (fwfile == NULL) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering int err;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering /* This link indicates the missing firmware file and the associated device */
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering info(udev, "did not find firmware file '%s'\n", firmware);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering do {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering err = util_create_path(udev, misspath);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (err != 0 && err != -ENOENT)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering break;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering udev_selinux_setfscreatecon(udev, misspath, S_IFLNK);
ce1dde29b92d1399ce502e0f7db790a99d14841fThomas Hindoe Paaboel Andersen err = symlink(devpath, misspath);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (err != 0)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering err = -errno;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering udev_selinux_resetfscreatecon(udev);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering } while (err == -ENOENT);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering rc = 2;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering set_loading(udev, loadpath, "-1");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering }
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering rc = 3;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering }
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (unlink(misspath) == 0)
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering util_delete_path(udev, misspath);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (!set_loading(udev, loadpath, "1"))
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering goto exit;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering util_strscpyl(datapath, sizeof(datapath), udev_get_sys_path(udev), devpath, "/data", NULL);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) {
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering err(udev, "error sending firmware '%s' to device\n", firmware);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering set_loading(udev, loadpath, "-1");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering rc = 4;
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poettering goto exit;
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poettering };
c7435cc9115f5c8166433fd5ece028c06360ecd1Lennart Poettering
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering set_loading(udev, loadpath, "0");
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poetteringexit:
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering udev_unref(udev);
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering udev_log_close();
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering return rc;
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering}
24a2bf4c9b0917231dd4f9b4289eabd46c382d3fLennart Poettering