/* * libbb/libbbd.c - Bitbang library daemon * * Written 2010-2011 by Werner Almesberger * Copyright 2010-2011 Werner Almesberger * * This program 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 2 of the License, or * (at your option) any later version. */ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <dirent.h> #include <poll.h> #include <errno.h> #include <syslog.h> #include <sys/types.h> #include <sys/socket.h> #include "libbb.h" #define FD_DIR "/proc/self/fd" #define DRIVER_NAME "jz4740-mmc.0" #define UNBIND_PATH "/sys/bus/platform/drivers/jz4740-mmc/unbind" #define BIND_PATH "/sys/bus/platform/drivers/jz4740-mmc/bind" static void closefds(void) { DIR *dir; const struct dirent *de; int fd; dir = opendir(FD_DIR); if (!dir) { perror(FD_DIR); exit(1); } while ((de = readdir(dir))) { if (*de->d_name == '.') continue; fd = atoi(de->d_name); if (fd == 1 || fd == 2) continue; if (fd == dirfd(dir)) continue; (void) close(fd); } closedir(dir); } static int echo(const char *msg, const char *path) { int fd; ssize_t wrote; int len = strlen(msg); fd = open(path, O_WRONLY); if (fd < 0) { perror(path); return -errno; } wrote = write(fd, msg, len); if (wrote < 0) { perror(path); return -errno; } if (wrote != len) { fprintf(stderr, "short write: %d < %d\n", (int) wrote, len); return -ENOSPC; } if (close(fd) < 0) { perror(path); return -errno; } return 0; } static void lock_area(int fd, off_t from, off_t len) { if (lseek(fd, from, SEEK_SET) == (off_t) -1) { perror("lseek"); exit(1); } if (lockf(fd, F_LOCK, len) < 0) { perror("lockf"); exit(1); } } static int send_fd(int s, int fd) { struct msghdr msg; struct cmsg { struct cmsghdr hdr; int fd; } cmsg; memset(&msg, 0, sizeof(msg)); msg.msg_control = &cmsg; msg.msg_controllen = sizeof(cmsg); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_RIGHTS; cmsg.hdr.cmsg_len = sizeof(cmsg); cmsg.fd = fd; if (sendmsg(s, &msg, 0) >= 0) return 0; perror("sendmsg"); return -1; } static int wait_for_close(int s) { struct pollfd fds[1]; int res; fds->fd = s; fds->events = 0; fds->revents = POLLHUP; res = poll(fds, 1, -1); if (res < 0) { perror("poll"); return -1; } if (!res) { fprintf(stderr, "poll inexplicably returned 0\n"); return -1; } return 0; } int main(int argc, char **argv) { int fd, res; int exit_code = 1; fprintf(stderr, "setsid\n"); if (setsid() == (pid_t) -1) { perror("setsid"); exit(1); } fprintf(stderr, "closefds\n"); closefds(); fprintf(stderr, "open devmem\n"); fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd < 0) { perror("/dev/mem"); exit(1); } fprintf(stderr, "lock_area\n"); lock_area(fd, LIBBB_GPIO_BASE+LIBBB_PORT_D_OFFSET, LIBBB_PORT_D_WINDOW); fprintf(stderr, "echo (unbind)\n"); if (echo(DRIVER_NAME "\n", UNBIND_PATH) < 0) exit(1); fprintf(stderr, "send_fd\n"); if (send_fd(1, fd) < 0) goto cleanup; fprintf(stderr, "wait_for_close\n"); if (wait_for_close(1) < 0) goto cleanup; exit_code = 0; cleanup: fprintf(stderr, "echo (bind)n"); res = echo(DRIVER_NAME "\n", BIND_PATH); if (res < 0) { /* stderr may no longer exist. report to syslog. */ openlog("libbbd", LOG_CONS, LOG_USER); syslog(LOG_CRIT, "libbd failed to re-bind driver (%d)", res); } fprintf(stderr, "exit\n"); return exit_code; }