/* * Copyright (c) 2011-2013 Douglas Gilbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /***************************************************************** * g20tc_freq.c * * Utility to produce the given frequencies for the given durations. * Uses the AT91SAM9G20 timer-counter (TC) unit to generate the * frequencies on PB0, PB1, PB2, PB3, PB18 or PB19 GPIO lines. * These belong to TC3, TC4 and TC5. The lower number timer-counters * (i.e. TC0, TC1 and TC2) are left for the kernel to use. * ****************************************************************/ #define _XOPEN_SOURCE 500 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include static const char * version_str = "1.13 20130721"; #define MAX_ELEMS 256 #define DEV_MEM "/dev/mem" #define MAP_SIZE 4096 /* assume to be power of 2 */ #define MAP_MASK (MAP_SIZE - 1) #define PIO_PER_B 0xfffff600 /* enable normal gpio on PB */ #define PIO_PDR_B 0xfffff604 /* disable normal gpio on PB */ #define PIO_BSR_B 0xfffff674 /* select peripheral B on PB */ #define PMC_PCER 0xfffffc10 /* enable internal clock to TC? */ #define PMC_PCDR 0xfffffc14 /* disable internal clock to TC? */ // wave=1, wavesel=2, T_CLK1 EEVT=1 ASWTRG=2 ACPC=2 ACPA=1 // BSWTRG=2 BCPC=2 BCPB=1 #define TC_CMR_VAL_CLK1 0x8989c400 // wave=1, wavesel=2, T_CLK4 EEVT=1 ASWTRG=2 ACPC=2 ACPA=1 // BSWTRG=2 BCPC=2 BCPB=1 #define TC_CMR_VAL_CLK4 0x8989c403 #define TC_CMR_VAL_CLK5 0x8989c404 #define CLK1_NUMERATOR (66 * 1000000) #define CLK4_NUMERATOR 1031250 #define CLK5_NUMERATOR 32768 struct mmap_state { void * mmap_ptr; off_t prev_mask_addrp; int mmap_ok; }; /* when both members are zero then end of elem_arr */ struct elem_t { int frequency; /* > 0 then Hz; < 0 then period in ms; 0 then low */ int duration_ms; /* > 0 then duration in ms; -1 then continual */ }; struct table_io_t { int pb_val; /* PB this entry is for */ unsigned int pio_mask; unsigned int tc_ccr; unsigned int tc_cmr; unsigned int tc_ra; unsigned int tc_rb; unsigned int tc_rc; unsigned int pmc_pcer_val; }; static struct elem_t elem_arr[MAX_ELEMS]; static struct table_io_t table_arr[] = { { 0, 0x1, 0xfffdc000, 0xfffdc004, 0xfffdc014, 0, 0xfffdc01c, 0x4000000}, { 1, 0x2, 0xfffdc000, 0xfffdc004, 0, 0xfffdc018, 0xfffdc01c, 0x4000000}, { 2, 0x4, 0xfffdc040, 0xfffdc044, 0xfffdc054, 0, 0xfffdc05c, 0x8000000}, { 3, 0x8, 0xfffdc080, 0xfffdc084, 0xfffdc094, 0, 0xfffdc09c, 0x10000000}, { 18, 0x40000, 0xfffdc040, 0xfffdc044, 0, 0xfffdc058, 0xfffdc05c, 0x8000000}, { 19, 0x80000, 0xfffdc080, 0xfffdc084, 0, 0xfffdc098, 0xfffdc09c, 0x10000000}, { 0, 0, 0, 0, 0, 0, 0, 0}, }; static int cl_foreground = 1; static int verbose = 0; static void usage(void) { fprintf(stderr, "Usage: " "g20tc_freq [-b ] [-d] [-D] [-f ] [-h] [-i]\n" " [-n] [-p F1,D1[,F2,D2...]] [-u] [-v] [-V] " "[-z]\n" " where:\n" " -b 0 for PB0 (def), 1 for PB1, etc for " "PB0-3, PB18 or PB19\n" " TC3: PB0+PB1; TC4: PB2+PB18; TC5: " "PB3+PB19\n" " -d dummy mode: decode frequency,duration pairs, " "print\n" " them then exit; ignore PB\n" " -D after initial checks, run as daemon which " "exits after\n" " frequency(s) is produced\n" " -f obtain input from . of '-' " "taken as\n" " read stdin. If '-f' not given then '-p' " "option expected\n" " -h print usage message\n" " -i initialize PB for frequency output, " "(def: assume\n" " already set up). Line set low prior " "frequency generation\n" " -n no realtime scheduling (def: set " "SCHED_FIFO)\n" " -p F1,D1[,F2,D2...] one or more frequency duration " "pairs; frequency\n" " in Hz and the duration in " "milliseconds\n" " -u restore PB to normal GPIO operation, " "(def: leave\n" " low for next frequency generation)\n" " -v increase verbosity (multiple times for more)\n" " -V print version string then exit\n" " -z turn off PMC clock to TC associated with " "PB.\n" " This saves a bit of power\n" "Use the timer counter (TC) in the AT91SAM9G20 to generate " "frequencies.\n" "List of frequency (Hz), duration (millisecond) pairs can be " "given on the\ncommand line ('-p') or in a file ('-f'). Use a " "frequency of 0 for a delay\n(of silence). The first time " "this utility is called option '-i' probably\nshould " "be given to initialize the TC. Duration of -1 for " "continuous\n(exit and maintain), assumes '-i'. 0.5 Hz " "(freq=-1) to 33 MHz.\n"); } void cl_print(int priority, const char *fmt, ...) { va_list ap; va_start(ap,fmt); if (cl_foreground) vfprintf(stderr, fmt, ap); else vsyslog(priority, fmt, ap); va_end(ap); } /* Daemonize the current process. */ void cl_daemonize(const char * name, int no_chdir, int no_varrunpid, int verbose) { pid_t pid, sid, my_pid; char b[64]; FILE * fp; /* already a daemon */ if (getppid() == 1 ) return; pid = fork(); if (pid < 0) { strcpy(b, name); strcat(b, " fork"); perror(b); exit(EXIT_FAILURE); } if (pid > 0) exit(EXIT_SUCCESS); /* Cancel certain signals */ signal(SIGCHLD, SIG_DFL); /* A child process dies */ signal(SIGTSTP, SIG_IGN); /* Various TTY signals */ signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ signal(SIGTERM, SIG_DFL); /* Die on SIGTERM */ umask(0); sid = setsid(); if (sid < 0) { cl_print(LOG_ERR, "setsid: %s\n", strerror(errno)); exit(EXIT_FAILURE); } if (! no_chdir) { if ((chdir("/")) < 0) { cl_print(LOG_ERR, "chdir(/): %s\n", strerror(errno)); exit(EXIT_FAILURE); } } fp = freopen("/dev/null", "r", stdin); fp = freopen("/dev/null", "w", stdout); fp = freopen("/dev/null", "w", stderr); /* ignoring handle */ if (! no_varrunpid) { my_pid = getpid(); snprintf(b, sizeof(b), "/var/run/%s.pid", name); fp = fopen(b, "w+"); if (fp) { snprintf(b, sizeof(b), "%d\n", my_pid); fputs(b, fp); fclose(fp); } else if (verbose) cl_print(LOG_WARNING, "Unable to open %s to put my pid(%d) in\n", b, my_pid); } } /* Returns > 0 on success, 0 on error */ unsigned int get_num(const char * buf, int len) { char b[68]; int res; unsigned int unum; if (len > (int)(sizeof(b) - 4)) { fprintf(stderr, "get_num: len=%d too large for a number\n", len); return 0; /* no good return, 0 is least harmful */ } memcpy(b, buf, len); b[len] = '\0'; res = sscanf(b, "%u", &unum); if (1 != res) { fprintf(stderr, "get_num: could not decode number\n"); return 0; } return unum; } /* Read pairs of numbers from command line (comma (or (single) space) * separated list) or from stdin or file (one or two per line, comma * separated list or space separated list). Numbers assumed to be decimal. * Returns 0 if ok, or 1 if error. */ static int build_arr(FILE * fp, const char * inp, struct elem_t * arr, int max_arr_len) { int in_len, k, j, m, fr, got_freq, neg; unsigned int u; const char * lcp; const char * allowp; if ((NULL == arr) || (max_arr_len < 1)) return 1; allowp = "-0123456789 ,\t"; if (fp) { /* read from file or stdin */ char line[512]; int off = 0; for (j = 0, fr = 0; j < 512; ++j) { if (NULL == fgets(line, sizeof(line), fp)) break; in_len = strlen(line); if (in_len > 0) { if ('\n' == line[in_len - 1]) { --in_len; line[in_len] = '\0'; } } if (0 == in_len) continue; lcp = line; m = strspn(lcp, " \t"); if (m == in_len) continue; lcp += m; in_len -= m; if ('#' == *lcp) continue; k = strspn(lcp, allowp); if ((k < in_len) && ('#' != lcp[k])) { fprintf(stderr, "build_arr: syntax error at " "line %d, pos %d\n", j + 1, m + k + 1); return 1; } for (k = 0; k < 1024; ++k) { if ('#' == *lcp) { --k; break; } if ('-' == *lcp) { neg = 1; ++lcp; } else neg = 0; u = get_num(lcp, strlen(lcp)); if ((off + k) >= max_arr_len) { fprintf(stderr, "build_arr: array length exceeded\n"); return 1; } if (neg) { if ((u - 1) > INT_MAX) { fprintf(stderr, "build_arr: number too small: " "-%u\n", u); return 1; } m = -((int)(u - 1)); --m; } else { /* non-negative */ if (u > INT_MAX) { fprintf(stderr, "build_arr: number too large: " "%u\n", u); return 1; } m = (int)u; } got_freq = 0; if (fr) { fr = 0; arr[off + k].duration_ms = m; } else { fr = 1; arr[off + k].frequency = m; ++got_freq; --k; } lcp = strpbrk(lcp, " ,\t"); if (NULL == lcp) break; lcp += strspn(lcp, " ,\t"); if ('\0' == *lcp) break; } off += (k + 1); } if (fr) { fprintf(stderr, "build_arr: got frequency but missing " "duration\n"); return 1; } if (off < max_arr_len) { arr[off].frequency = 0; arr[off].duration_ms = 0; } } else if (inp) { /* list of numbers on command line */ lcp = inp; in_len = strlen(inp); if (0 == in_len) { arr[0].frequency = 0; arr[0].duration_ms = 0; return 0; } k = strspn(inp, allowp); if (in_len != k) { fprintf(stderr, "build_arr: error at pos %d\n", k + 1); return 1; } for (k = 0, fr = 0; k < max_arr_len; ++k) { if ('-' == *lcp) { neg = 1; ++lcp; } else neg = 0; u = get_num(lcp, strlen(lcp)); if (neg) { if ((u - 1) > INT_MAX) { fprintf(stderr, "build_arr: number too small: " "-%u\n", u); return 1; } m = -((int)(u - 1)); --m; } else { /* non-negative */ if (u > INT_MAX) { fprintf(stderr, "build_arr: number too large: " "%u\n", u); return 1; } m = (int)u; } got_freq = 0; if (fr) { fr = 0; arr[k].duration_ms = m; } else { fr = 1; arr[k].frequency = m; ++got_freq; --k; } lcp = strpbrk(lcp, " ,\t"); if (NULL == lcp) break; lcp += strspn(lcp, " ,\t"); if ('\0' == *lcp) break; } if (fr) { fprintf(stderr, "build_arr: got frequency but missing " "duration\n"); return 1; } if (k == max_arr_len) { fprintf(stderr, "build_arr: array length exceeded\n"); return 1; } if (k < (max_arr_len - 1)) { arr[k + 1].frequency = 0; arr[k + 1].duration_ms = 0; } } return 0; } /* Since we map a whole page (MAP_SIZE) then that page may already be * mapped which means we can skip a munmap() and mmap() call. Returns * new or re-used pointer to mmapped page, or returns NULL if problem. */ static void * check_mmap(int mem_fd, unsigned int wanted_addr, struct mmap_state * msp) { off_t mask_addr; mask_addr = (wanted_addr & ~MAP_MASK); if ((0 == msp->mmap_ok) || (msp->prev_mask_addrp != mask_addr)) { if (msp->mmap_ok) { if (-1 == munmap(msp->mmap_ptr, MAP_SIZE)) { fprintf(stderr, "mmap_ptr=%p:\n", msp->mmap_ptr); perror(" munmap"); return NULL; } else if (verbose > 2) fprintf(stderr, "munmap() ok, mmap_ptr=%p\n", msp->mmap_ptr); } msp->mmap_ptr = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, mask_addr); if ((void *)-1 == msp->mmap_ptr) { msp->mmap_ok = 0; fprintf(stderr, "addr=0x%x, mask_addr=0x%lx :\n", wanted_addr, mask_addr); perror(" mmap"); return NULL; } msp->mmap_ok = 1; msp->prev_mask_addrp = mask_addr; if (verbose > 2) fprintf(stderr, "mmap() ok, mask_addr=0x%lx, mmap_ptr=%p\n", mask_addr, msp->mmap_ptr); } return msp->mmap_ptr; } int main(int argc, char * argv[]) { int mem_fd, res, k, opt, rms, rc, prev_rms, tc_clk_ena, clk_num, want_clk; unsigned int tc_ms; int have_continuous = 0; int dummy = 0; int do_daemon = 0; int pb_num = 0; int do_init = 0; int no_sched = 0; int do_uninit = 0; int do_clock_off = 0; const char * fname = NULL; const char * pstring = NULL; struct elem_t * ep; struct table_io_t * tp; void * mmap_ptr = (void *)-1; void * ap; struct timespec request; FILE * input_filep = NULL; struct sched_param spr; struct mmap_state mstat; mem_fd = -1; while ((opt = getopt(argc, argv, "b:dDf:hinp:uvVz")) != -1) { switch (opt) { break; case 'b': pb_num = atoi(optarg); break; case 'd': ++dummy; break; case 'D': ++do_daemon; break; case 'f': fname = optarg; break; case 'h': case '?': usage(); return 0; case 'i': ++do_init; break; case 'n': ++no_sched; break; case 'p': pstring = optarg; break; case 'u': ++do_uninit; break; case 'v': ++verbose; break; case 'V': fprintf(stderr, "version: %s\n", version_str); return 0; case 'z': ++do_clock_off; break; default: fprintf(stderr, "unrecognised option code 0x%x ??\n", opt); usage(); return 1; } } if (optind < argc) { if (optind < argc) { for (; optind < argc; ++optind) fprintf(stderr, "Unexpected extra argument: %s\n", argv[optind]); usage(); return 1; } } if (fname) { if ((1 == strlen(fname)) && ('-' == fname[0])) input_filep = stdin; else { input_filep = fopen(fname, "r"); if (NULL == input_filep) { fprintf(stderr, "failed to open %s: ", fname); perror("fopen()"); return 1; } } } if (fname || pstring) { res = build_arr(input_filep, pstring, elem_arr, MAX_ELEMS - 1); if (res) { fprintf(stderr, "build_arr() failed\n"); return 1; } } if (dummy || (verbose > 1)) { printf("build_arr after command line input processing:\n"); for (k = 0; ((0 != elem_arr[k].frequency) || (0 != elem_arr[k].duration_ms)); ++k) { ep = elem_arr + k; if (ep->frequency > 0) printf(" frequency: %d Hz,", ep->frequency); else if (ep->frequency < 0) { if (-1 == ep->frequency) printf(" period: 2 secs,"); else printf(" period: %d is bad,", -ep->frequency); } else printf(" line is low,"); if (-1 == ep->duration_ms) printf("\tduration: continual\n"); else if (ep->duration_ms > 0) printf("\tduration: %d ms\n", ep->duration_ms); else printf("\tduration: %d is bad\n", ep->duration_ms); } if (dummy) return 0; } if ((0 == elem_arr[0].frequency) && (0 == elem_arr[0].duration_ms) && (0 == do_init) && (0 == do_uninit) && (0 == do_clock_off)) { printf("Nothing to do so exit. Add '-h' for usage.\n"); return 0; } for (tp = table_arr; tp->pio_mask; ++ tp) { if (pb_num == tp->pb_val) break; } if (0 == tp->pio_mask) { fprintf(stderr, "-p invalid. Expect 0, 1, 2, 3, 18 or 19\n"); usage(); return 1; } if ((mem_fd = open(DEV_MEM, O_RDWR | O_SYNC)) < 0) { perror("open of " DEV_MEM " failed"); return 1; } else if (verbose) printf("open(" DEV_MEM ", O_RDWR | O_SYNC) okay\n"); memset(&mstat, 0, sizeof(mstat)); if (do_daemon) cl_daemonize("g20tc_freq", 1, 1, verbose); if (0 == no_sched) { k = sched_get_priority_min(SCHED_FIFO); if (k < 0) cl_print(LOG_ERR, "sched_get_priority_min: %s\n", strerror(errno)); else { spr.sched_priority = k; if (sched_setscheduler(0, SCHED_FIFO, &spr) < 0) cl_print(LOG_ERR, "sched_setscheduler: %s\n", strerror(errno)); } } for (k = 0; (elem_arr[k].frequency || elem_arr[k].duration_ms); ++k) { if (-1 == elem_arr[k].duration_ms) { ++have_continuous; break; } } if (do_init || have_continuous) { if (NULL == ((mmap_ptr = check_mmap(mem_fd, tp->tc_ccr, &mstat)))) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_ccr & MAP_MASK); *((unsigned long *)ap) = 0x2; if (verbose > 1) fprintf(stderr, "wrote: TC_CCR addr=0x%x, val=0x%x\n", tp->tc_ccr, 0x2); if (NULL == ((mmap_ptr = check_mmap(mem_fd, PIO_PDR_B, &mstat)))) return 1; ap = (unsigned char *)mmap_ptr + (PIO_PDR_B & MAP_MASK); *((unsigned long *)ap) = tp->pio_mask; if (verbose > 1) fprintf(stderr, "wrote: PIO_PDR addr=0x%x, val=0x%x\n", PIO_PDR_B, tp->pio_mask); if (NULL == ((mmap_ptr = check_mmap(mem_fd, PIO_BSR_B, &mstat)))) return 1; ap = (unsigned char *)mmap_ptr + (PIO_BSR_B & MAP_MASK); *((unsigned long *)ap) = tp->pio_mask; if (verbose > 1) fprintf(stderr, "wrote: PIO_BSR addr=0x%x, val=0x%x\n", PIO_BSR_B, tp->pio_mask); if (NULL == ((mmap_ptr = check_mmap(mem_fd, PMC_PCER, &mstat)))) return 1; ap = (unsigned char *)mmap_ptr + (PMC_PCER & MAP_MASK); *((unsigned long *)ap) = tp->pmc_pcer_val; if (verbose > 1) fprintf(stderr, "wrote: PMC_PCER addr=0x%x, val=0x%x\n", PMC_PCER, tp->pmc_pcer_val); if ((0 == elem_arr[0].frequency) && (0 == elem_arr[0].duration_ms) && (0 == do_uninit) && (0 == do_clock_off)) { // nothing else to do, may need to kick it to assure line is low ; } } for (k = 0, prev_rms = 0, tc_clk_ena = 0, clk_num = 0; (elem_arr[k].frequency || elem_arr[k].duration_ms); ++k) { ep = elem_arr + k; if (0 != ep->frequency) { if (ep->frequency < 0) { rc = 65535; /* negatives --> 0.5 Hz */ want_clk = 5; } else { rc = CLK4_NUMERATOR / ep->frequency; if (rc > 65535) { rc = CLK5_NUMERATOR / ep->frequency; want_clk = 5; /* 1 Hz to 15 Hz */ } else if (rc < 50) { rc = CLK1_NUMERATOR / ep->frequency; if (rc < 2) { fprintf(stderr, "frequency[%d]=%d too high, limit: " "%d Hz (CLK1)\n", k + 1, ep->frequency, CLK1_NUMERATOR / 2); return 1; } want_clk = 1; /* 20 kHz to 33 MHz */ } else want_clk = 4; /* 15 Hz to 20 kHz */ } if (clk_num != want_clk) { mmap_ptr = check_mmap(mem_fd, tp->tc_cmr, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_cmr & MAP_MASK); switch (want_clk) { case 1: *((unsigned long *)ap) = TC_CMR_VAL_CLK1; break; case 4: *((unsigned long *)ap) = TC_CMR_VAL_CLK4; break; case 5: *((unsigned long *)ap) = TC_CMR_VAL_CLK5; break; default: fprintf(stderr, "frequency[%d]=%u, bad want_clk=%d\n", k + 1, ep->frequency, want_clk); return 1; } clk_num = want_clk; if (verbose > 1) fprintf(stderr, "wrote: TC_CMR addr=0x%x, val=0x%lx\n", tp->tc_cmr, *((unsigned long *)ap)); } rms = rc / 2; // use 1:1 mark space ratio // >>>>>>>>>>>>>>>>>>>>>>>>>> // rms = rc * 1 / 5; // use 4:1 mark space ratio // rms = rc * 4 / 5; // use 1:4 mark space ratio if (tp->tc_ra) tc_ms = tp->tc_ra; else tc_ms = tp->tc_rb; if (rc > prev_rms) { // set up RC prior to RA (or RB) mmap_ptr = check_mmap(mem_fd, tp->tc_rc, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_rc & MAP_MASK); *((unsigned long *)ap) = rc; if (verbose > 1) fprintf(stderr, "wrote: TR_RC addr=0x%x, val=0x%x\n", tp->tc_rc, rc); mmap_ptr = check_mmap(mem_fd, tc_ms, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tc_ms & MAP_MASK); *((unsigned long *)ap) = rms; if (verbose > 1) fprintf(stderr, "wrote: %s addr=0x%x, val=0x%x\n", (tp->tc_ra ? "TC_RA" : "TC_RB"), tc_ms, rms); } else { // set up RA or RB prior to RC mmap_ptr = check_mmap(mem_fd, tc_ms, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tc_ms & MAP_MASK); *((unsigned long *)ap) = rms; if (verbose > 1) fprintf(stderr, "wrote: %s addr=0x%x, val=0x%x\n", (tp->tc_ra ? "TC_RA" : "TC_RB"), tc_ms, rms); mmap_ptr = check_mmap(mem_fd, tp->tc_rc, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_rc & MAP_MASK); *((unsigned long *)ap) = rc; if (verbose > 1) fprintf(stderr, "wrote: TC_RC addr=0x%x, val=0x%x\n", tp->tc_rc, rc); } prev_rms = rms; if (0 == tc_clk_ena) { // everything should be set up, start it ... mmap_ptr = check_mmap(mem_fd, tp->tc_ccr, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_ccr & MAP_MASK); // enable clock (0x1) ORed with software trigger (0x4) *((unsigned long *)ap) = 0x5; if (verbose > 1) fprintf(stderr, "wrote: TC_CCR addr=0x%x, val=0x%x\n", tp->tc_ccr, 0x5); tc_clk_ena = 1; } } else if (tc_clk_ena) { // drive line low mmap_ptr = check_mmap(mem_fd, tp->tc_ccr, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_ccr & MAP_MASK); // disable clock (0x1) ORed with software trigger (0x4) *((unsigned long *)ap) = 0x6; if (verbose > 1) fprintf(stderr, "wrote: TC_CCR addr=0x%x, val=0x%x\n", tp->tc_ccr, 0x6); tc_clk_ena = 0; } if (ep->duration_ms < 0) break; else if (ep->duration_ms > 0) { request.tv_sec = ep->duration_ms / 1000; request.tv_nsec = (ep->duration_ms % 1000) * 1000000; if (nanosleep(&request, NULL) < 0) { perror("nanosleep"); return 1; } if (verbose > 1) fprintf(stderr, "slept for %d milliseconds\n", ep->duration_ms); } } if ((tc_clk_ena) && (0 == have_continuous)) { // drive line low mmap_ptr = check_mmap(mem_fd, tp->tc_ccr, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_ccr & MAP_MASK); // disable clock (0x1) ORed with software trigger (0x4) *((unsigned long *)ap) = 0x6; if (verbose > 1) fprintf(stderr, "wrote: TC_CCR addr=0x%x, val=0x%x\n", tp->tc_ccr, 0x6); tc_clk_ena = 0; } if (do_uninit) { // disable clock within TC mmap_ptr = check_mmap(mem_fd, tp->tc_ccr, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (tp->tc_ccr & MAP_MASK); *((unsigned long *)ap) = 0x2; if (verbose > 1) fprintf(stderr, "wrote: TC_CCR addr=0x%x, val=0x%x\n", tp->tc_ccr, 0x2); // Enable normal GPIO action on PB mmap_ptr = check_mmap(mem_fd, PIO_PER_B, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (PIO_PER_B & MAP_MASK); *((unsigned long *)ap) = tp->pio_mask; if (verbose > 1) fprintf(stderr, "wrote: PIO_PER addr=0x%x, val=0x%x\n", PIO_PER_B, tp->pio_mask); } if (do_clock_off) { // disable clock in Power Management Controller going to TC? mmap_ptr = check_mmap(mem_fd, PMC_PCDR, &mstat); if (NULL == mmap_ptr) return 1; ap = (unsigned char *)mmap_ptr + (PMC_PCDR & MAP_MASK); *((unsigned long *)ap) = tp->pmc_pcer_val; if (verbose > 1) fprintf(stderr, "wrote: PMC_PCDR addr=0x%x, val=0x%x\n", PMC_PCDR, tp->pmc_pcer_val); } if (mstat.mmap_ok) { if (-1 == munmap(mmap_ptr, MAP_SIZE)) { fprintf(stderr, "mmap_ptr=%p:\n", mmap_ptr); perror(" munmap"); return 1; } else if (verbose > 2) fprintf(stderr, "trailing munmap() ok, mmap_ptr=%p\n", mmap_ptr); } if (mem_fd >= 0) close(mem_fd); return 0; }