From 458702cf54a94a1127a37519269b92638802bcda Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Sat, 27 Nov 2010 20:21:33 -0300 Subject: [PATCH] libbb: library for bit-banging the Ben's uSD port (in progress) This doesn't work with old kernels, but 2.6.36 should be better. Haven't tried it yet, though. --- libbb/Makefile | 35 +++++++++ libbb/libbb.c | 126 ++++++++++++++++++++++++++++++++ libbb/libbbd.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 libbb/Makefile create mode 100644 libbb/libbb.c create mode 100644 libbb/libbbd.c diff --git a/libbb/Makefile b/libbb/Makefile new file mode 100644 index 0000000..dbaead7 --- /dev/null +++ b/libbb/Makefile @@ -0,0 +1,35 @@ +CC = mipsel-openwrt-linux-uclibc-gcc + +PREFIX = /usr/local + +LIBBD_PATH = $(PREFIX)/lib/libbbd + +CFLAGS = -Wall -DLIBBD_PATH='"$(LIBBD_PATH)"' + +MAIN = libbb.a libbbd + +.PHONY: all install uninstall clean spotless upload + +all: $(MAIN) + +libbb.a: libbb.o + $(AR) cr $@ $^ + +libbb.o libbd: libbb.h + +install: $(MAIN) + install -D libbb.a $(PREFIX)/lib/ + install -D libbbd $(LIBBD_PATH) + +uninstall: + rm -f $(PREFIX)/lib/libbb.a + rm -f $(LIBBD_PATH) + +clean: + rm -f libbb.o + +spotless: clean + rm -f $(MAIN) + +upload: libbbd + scp libbbd ben:/usr/local/lib/ diff --git a/libbb/libbb.c b/libbb/libbb.c new file mode 100644 index 0000000..614fd2e --- /dev/null +++ b/libbb/libbb.c @@ -0,0 +1,126 @@ +/* + * libbb/libbb.c - Bitbang library + * + * Written 2010 by Werner Almesberger + * Copyright 2010 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 +#include +#include +#include +#include +#include +//#include +#include + +#include "libbb.h" + + +#define PAGE_SIZE 4096 + + +volatile void *libbb_mem; + +static int bbd_fd = -1; + + +static int recv_fd(int s) +{ + 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); + if (recvmsg(s, &msg, 0) < 0) { + perror("recvmsg"); + exit(1); + } + return cmsg.fd; +} + + +static void child(void) +{ + execl(LIBBD_PATH, LIBBD_PATH, NULL); + perror(LIBBD_PATH); + _exit(1); +} + + +#if 0 +static int recv_fd(int s) +{ + pid_t res; + + res = waitpid(pid, &status, 0); + if (res < 0) { + perror("waitpid"); + exit(1); + } + if (res != pid) { + fprintf(stderr, "waitpid returned %d for %d\n", + (int) res, (int) pid); + exit(1); + } +} +#endif + + +static int bbd(void) +{ + int s[2]; + pid_t pid; + + if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, s) < 0) { + perror("socketpair"); + exit(1); + } + if (dup2(s[1], 1) < 0) { + perror("dup2"); + exit(1); + } + + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + if (pid) + return recv_fd(s[0]); + else + child(); + return -1; /* can't happen */ +} + + +void libbb_open(void) +{ + bbd_fd = bbd(); + libbb_mem = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, + bbd_fd, LIBBB_GPIO_BASE); + if (libbb_mem == MAP_FAILED) { + perror("mmap"); + exit(1); + } +} + + + +void libbb_close(void) +{ + if (close(bbd_fd) < 0) { + perror("close"); + exit(1); + } +} diff --git a/libbb/libbbd.c b/libbb/libbbd.c new file mode 100644 index 0000000..a2cd5af --- /dev/null +++ b/libbb/libbbd.c @@ -0,0 +1,193 @@ +/* + * libbb/libbbd.c - Bitbang library daemon + * + * Written 2010 by Werner Almesberger + * Copyright 2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + 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; +}