Werner's Miscellanea
Sign in or create your account | Project List | Help
Werner's Miscellanea Git Source Tree
Root/
| 1 | /* |
| 2 | * libbb/libbbd.c - Bitbang library daemon |
| 3 | * |
| 4 | * Written 2010-2011 by Werner Almesberger |
| 5 | * Copyright 2010-2011 Werner Almesberger |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | */ |
| 12 | |
| 13 | |
| 14 | #include <stdlib.h> |
| 15 | #include <stdio.h> |
| 16 | #include <unistd.h> |
| 17 | #include <string.h> |
| 18 | #include <fcntl.h> |
| 19 | #include <dirent.h> |
| 20 | #include <poll.h> |
| 21 | #include <errno.h> |
| 22 | #include <syslog.h> |
| 23 | #include <sys/types.h> |
| 24 | #include <sys/socket.h> |
| 25 | |
| 26 | #include "libbb.h" |
| 27 | |
| 28 | |
| 29 | #define FD_DIR "/proc/self/fd" |
| 30 | |
| 31 | #define DRIVER_NAME "jz4740-mmc.0" |
| 32 | #define UNBIND_PATH "/sys/bus/platform/drivers/jz4740-mmc/unbind" |
| 33 | #define BIND_PATH "/sys/bus/platform/drivers/jz4740-mmc/bind" |
| 34 | |
| 35 | |
| 36 | static void closefds(void) |
| 37 | { |
| 38 | DIR *dir; |
| 39 | const struct dirent *de; |
| 40 | int fd; |
| 41 | |
| 42 | dir = opendir(FD_DIR); |
| 43 | if (!dir) { |
| 44 | perror(FD_DIR); |
| 45 | exit(1); |
| 46 | } |
| 47 | while ((de = readdir(dir))) { |
| 48 | if (*de->d_name == '.') |
| 49 | continue; |
| 50 | fd = atoi(de->d_name); |
| 51 | if (fd == 1 || fd == 2) |
| 52 | continue; |
| 53 | if (fd == dirfd(dir)) |
| 54 | continue; |
| 55 | (void) close(fd); |
| 56 | } |
| 57 | closedir(dir); |
| 58 | } |
| 59 | |
| 60 | |
| 61 | static int echo(const char *msg, const char *path) |
| 62 | { |
| 63 | int fd; |
| 64 | ssize_t wrote; |
| 65 | int len = strlen(msg); |
| 66 | |
| 67 | fd = open(path, O_WRONLY); |
| 68 | if (fd < 0) { |
| 69 | perror(path); |
| 70 | return -errno; |
| 71 | } |
| 72 | wrote = write(fd, msg, len); |
| 73 | if (wrote < 0) { |
| 74 | perror(path); |
| 75 | return -errno; |
| 76 | } |
| 77 | if (wrote != len) { |
| 78 | fprintf(stderr, "short write: %d < %d\n", (int) wrote, len); |
| 79 | return -ENOSPC; |
| 80 | } |
| 81 | if (close(fd) < 0) { |
| 82 | perror(path); |
| 83 | return -errno; |
| 84 | } |
| 85 | return 0; |
| 86 | } |
| 87 | |
| 88 | |
| 89 | static void lock_area(int fd, off_t from, off_t len) |
| 90 | { |
| 91 | if (lseek(fd, from, SEEK_SET) == (off_t) -1) { |
| 92 | perror("lseek"); |
| 93 | exit(1); |
| 94 | } |
| 95 | if (lockf(fd, F_LOCK, len) < 0) { |
| 96 | perror("lockf"); |
| 97 | exit(1); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | |
| 102 | static int send_fd(int s, int fd) |
| 103 | { |
| 104 | struct msghdr msg; |
| 105 | struct cmsg { |
| 106 | struct cmsghdr hdr; |
| 107 | int fd; |
| 108 | } cmsg; |
| 109 | |
| 110 | memset(&msg, 0, sizeof(msg)); |
| 111 | msg.msg_control = &cmsg; |
| 112 | msg.msg_controllen = sizeof(cmsg); |
| 113 | cmsg.hdr.cmsg_level = SOL_SOCKET; |
| 114 | cmsg.hdr.cmsg_type = SCM_RIGHTS; |
| 115 | cmsg.hdr.cmsg_len = sizeof(cmsg); |
| 116 | cmsg.fd = fd; |
| 117 | if (sendmsg(s, &msg, 0) >= 0) |
| 118 | return 0; |
| 119 | perror("sendmsg"); |
| 120 | return -1; |
| 121 | } |
| 122 | |
| 123 | |
| 124 | static int wait_for_close(int s) |
| 125 | { |
| 126 | struct pollfd fds[1]; |
| 127 | int res; |
| 128 | |
| 129 | fds->fd = s; |
| 130 | fds->events = 0; |
| 131 | fds->revents = POLLHUP; |
| 132 | res = poll(fds, 1, -1); |
| 133 | if (res < 0) { |
| 134 | perror("poll"); |
| 135 | return -1; |
| 136 | } |
| 137 | if (!res) { |
| 138 | fprintf(stderr, "poll inexplicably returned 0\n"); |
| 139 | return -1; |
| 140 | } |
| 141 | return 0; |
| 142 | } |
| 143 | |
| 144 | |
| 145 | int main(int argc, char **argv) |
| 146 | { |
| 147 | int fd, res; |
| 148 | int exit_code = 1; |
| 149 | |
| 150 | fprintf(stderr, "setsid\n"); |
| 151 | if (setsid() == (pid_t) -1) { |
| 152 | perror("setsid"); |
| 153 | exit(1); |
| 154 | } |
| 155 | fprintf(stderr, "closefds\n"); |
| 156 | closefds(); |
| 157 | |
| 158 | fprintf(stderr, "open devmem\n"); |
| 159 | fd = open("/dev/mem", O_RDWR | O_SYNC); |
| 160 | if (fd < 0) { |
| 161 | perror("/dev/mem"); |
| 162 | exit(1); |
| 163 | } |
| 164 | fprintf(stderr, "lock_area\n"); |
| 165 | lock_area(fd, LIBBB_GPIO_BASE+LIBBB_PORT_D_OFFSET, |
| 166 | LIBBB_PORT_D_WINDOW); |
| 167 | |
| 168 | fprintf(stderr, "echo (unbind)\n"); |
| 169 | if (echo(DRIVER_NAME "\n", UNBIND_PATH) < 0) |
| 170 | exit(1); |
| 171 | |
| 172 | fprintf(stderr, "send_fd\n"); |
| 173 | if (send_fd(1, fd) < 0) |
| 174 | goto cleanup; |
| 175 | |
| 176 | fprintf(stderr, "wait_for_close\n"); |
| 177 | if (wait_for_close(1) < 0) |
| 178 | goto cleanup; |
| 179 | |
| 180 | exit_code = 0; |
| 181 | |
| 182 | cleanup: |
| 183 | fprintf(stderr, "echo (bind)n"); |
| 184 | res = echo(DRIVER_NAME "\n", BIND_PATH); |
| 185 | if (res < 0) { |
| 186 | /* stderr may no longer exist. report to syslog. */ |
| 187 | openlog("libbbd", LOG_CONS, LOG_USER); |
| 188 | syslog(LOG_CRIT, "libbd failed to re-bind driver (%d)", res); |
| 189 | } |
| 190 | |
| 191 | fprintf(stderr, "exit\n"); |
| 192 | return exit_code; |
| 193 | } |
| 194 |
Branches:
master
