diff --git a/source/common.c b/source/common.c index e36b7ec..4bd2463 100644 --- a/source/common.c +++ b/source/common.c @@ -131,7 +131,7 @@ pins_t pins_info[] = { { "CSID7", "U14_38", 139, BASE_METHOD_AS_IS, -1, -1}, { "GND", "U14_39", 0, BASE_METHOD_AS_IS, -1, -1}, { "GND", "U14_40", 0, BASE_METHOD_AS_IS, -1, -1}, - { NULL, NULL, 0 } + { NULL, NULL, 0, 0, -1, -1} }; @@ -178,10 +178,10 @@ int get_xio_base(void) /* Found the expander, get the contents of base */ snprintf(base_file, sizeof(base_file), "%s/%s/base", GPIO_PATH, ent->d_name); BUF2SMALL(base_file); base_fp = fopen(base_file, "r"); ASSRT(base_fp != NULL); - s = fgets(input_line, sizeof(input_line), base_fp); - ASSRT(s == input_line && strlen(input_line) < sizeof(input_line)-1); + s = fgets(input_line, sizeof(input_line), base_fp); BUF2SMALL(input_line); ASSRT(s); fclose(base_fp); - xio_base_address = atoi(input_line); /* remember the result */ + /* Remember the value in the static local. */ + xio_base_address = atoi(input_line); ASSRT(xio_base_address > 0); num_get_xio_base++; /* for self-test purposes */ } } /* if label file is open */ @@ -377,7 +377,7 @@ int build_path(const char *partial_path, const char *prefix, char *full_path, si dp = opendir (partial_path); if (dp != NULL) { while ((ep = readdir (dp))) { - // Enforce that the prefix must be the first part of the file + // Enforce that the prefix must be the first part of the file name char* found_string = strstr(ep->d_name, prefix); if (found_string != NULL && (ep->d_name - found_string) == 0) { @@ -419,3 +419,75 @@ int get_spi_bus_path_number(unsigned int spi) return -1; } } + + +struct dyn_int_array { + int num_elements; + int *array; +}; + +void *dyn_int_array_create(int initial_num_elements, int initial_val) +{ + struct dyn_int_array *new_array = (struct dyn_int_array *)malloc(sizeof(struct dyn_int_array)); + ASSRT(new_array != NULL); + new_array->num_elements = initial_num_elements; + new_array->array = (int *)malloc(initial_num_elements * sizeof(int)); + ASSRT(new_array->array != NULL); + int i; + for (i = 0; i < initial_num_elements; i++) + new_array->array[i] = initial_val; + + return (void *)new_array; +} /* dyn_int_array_create */ + + +void dyn_int_array_set(void **in_array, int i, int val, int initial_val) +{ + struct dyn_int_array *array = (struct dyn_int_array *)(*in_array); + if (array == NULL) + array = dyn_int_array_create(i * 3 / 2, initial_val); + + if (i >= array->num_elements) { + int new_num_elements = i * 3 / 2; /* half-again larger than current request */ + array->array = realloc(array->array, new_num_elements * sizeof(int)); + ASSRT(array->array != NULL); + // Zero out the newly allocated elements. + while (array->num_elements < new_num_elements) { + array->array[array->num_elements] = initial_val; + array->num_elements ++; + } + } + + array->array[i] = val; + *in_array = (void *)array; +} /* dyn_int_array_set */ + + +int dyn_int_array_get(void **in_array, int i, int initial_val) +{ + struct dyn_int_array *array = (struct dyn_int_array *)(*in_array); + if (array == NULL) + array = dyn_int_array_create(i * 3 / 2, initial_val); + + if (i >= array->num_elements) { + int new_num_elements = i * 3 / 2; /* half-again larger than current request */ + array->array = realloc(array->array, new_num_elements * sizeof(int)); + ASSRT(array->array != NULL); + // Zero out the newly allocated elements. + while (array->num_elements < new_num_elements) { + array->array[array->num_elements] = initial_val; + array->num_elements ++; + } + } + + return array->array[i]; + *in_array = (void *)array; +} /* dyn_int_array_get */ + + +void dyn_int_array_delete(void **in_array) +{ + struct dyn_int_array *array = (struct dyn_int_array *)(*in_array); + free(array->array); + free(array); +} /* dyn_int_array_delete */ diff --git a/source/common.h b/source/common.h index 54ae1e3..7fafeb3 100644 --- a/source/common.h +++ b/source/common.h @@ -41,7 +41,7 @@ SOFTWARE. // See http://blog.geeky-boy.com/2016/06/of-compiler-warnings-and-asserts-in.html #define ASSRT(cond_expr) do { \ if (!(cond_expr)) { \ - fprintf(stderr, "ASSRT failed at %s:%d (%s)", __FILE__, __LINE__, #cond_expr); \ + fprintf(stderr, "ASSRT failed at %s:%d (%s)\n", __FILE__, __LINE__, #cond_expr); \ fflush(stderr); \ abort(); \ } } while (0) @@ -75,6 +75,7 @@ int setup_error; int module_setup; int get_xio_base(void); +int gpio_number(pins_t *pin); int lookup_gpio_by_key(const char *key); int lookup_gpio_by_name(const char *name); int lookup_ain_by_key(const char *key); @@ -89,3 +90,6 @@ int get_pwm_key(const char *input, char *key); int get_adc_ain(const char *key, unsigned int *ain); int build_path(const char *partial_path, const char *prefix, char *full_path, size_t full_path_len); int get_spi_bus_path_number(unsigned int spi); +void dyn_int_array_set(void **in_array, int i, int val, int initial_val); +int dyn_int_array_get(void **in_array, int i, int initial_val); +void dyn_int_array_delete(void **in_array); diff --git a/source/event_gpio.c b/source/event_gpio.c index c5576f7..6f28f81 100644 --- a/source/event_gpio.c +++ b/source/event_gpio.c @@ -77,7 +77,7 @@ struct gpio_exp struct gpio_exp *exported_gpios = NULL; pthread_t threads; -int event_occurred[430] = { 0 }; +void *event_occurred = NULL; int thread_running = 0; int epfd = -1; @@ -92,8 +92,12 @@ int gpio_export(unsigned int gpio) return -1; } len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio); BUF2SMALL(str_gpio); - ssize_t s = write(fd, str_gpio, len); ASSRT(s == len); + ssize_t s = write(fd, str_gpio, len); close(fd); + if (s != len) + { + return -1; + } // add to list new_gpio = malloc(sizeof(struct gpio_exp)); @@ -183,8 +187,9 @@ int open_value_file(unsigned int gpio) // create file descriptor of value file snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); BUF2SMALL(filename); - if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) - return -1; + if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) { + return -1; + } add_fd_list(gpio, fd); return fd; } @@ -240,7 +245,6 @@ int gpio_set_direction(unsigned int gpio, unsigned int in_flag) } else { strncpy(direction, "in", ARRAY_SIZE(direction) - 1); } - ssize_t s = write(fd, direction, strlen(direction)); ASSRT(s == strlen(direction)); close(fd); return 0; @@ -285,8 +289,11 @@ int gpio_set_value(unsigned int gpio, unsigned int value) strncpy(vstr, "0", ARRAY_SIZE(vstr) - 1); } - ssize_t s = write(fd, vstr, strlen(vstr)); ASSRT(s == strlen(vstr)); + ssize_t s = write(fd, vstr, strlen(vstr)); close(fd); + + if (s != strlen(vstr)) + return -2; return 0; } @@ -454,11 +461,12 @@ void *poll_thread(void *threadarg) thread_running = 0; pthread_exit(NULL); } + // The return value represents the ending level after the edge. gpio = gpio_lookup(events.data.fd); if (gpio_initial(gpio)) { // ignore first epoll trigger set_initial_false(gpio); } else { - event_occurred[gpio] = 1; + dyn_int_array_set(&event_occurred, gpio, 1, 0); run_callbacks(gpio); } } @@ -512,6 +520,7 @@ int gpio_event_remove(unsigned int gpio) return 0; } +// add_edge_detect assumes the caller has ensured the GPIO is already exported. int add_edge_detect(unsigned int gpio, unsigned int edge) // return values: // 0 - Success @@ -528,31 +537,34 @@ int add_edge_detect(unsigned int gpio, unsigned int edge) return 1; // export /sys/class/gpio interface - gpio_export(gpio); gpio_set_direction(gpio, 0); // 0=input gpio_set_edge(gpio, edge); if (!fd) { - if ((fd = open_value_file(gpio)) == -1) + if ((fd = open_value_file(gpio)) == -1) { return 2; + } } // create epfd if not already open - if ((epfd == -1) && ((epfd = epoll_create(1)) == -1)) + if ((epfd == -1) && ((epfd = epoll_create(1)) == -1)) { return 2; + } // add to epoll fd ev.events = EPOLLIN | EPOLLET | EPOLLPRI; ev.data.fd = fd; - if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) { return 2; + } // start poll thread if it is not already running if (!thread_running) { - if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) + if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) { return 2; + } } return 0; @@ -576,13 +588,13 @@ void remove_edge_detect(unsigned int gpio) gpio_event_remove(gpio); // clear detected flag - event_occurred[gpio] = 0; + dyn_int_array_set(&event_occurred, gpio, 0, 0); } int event_detected(unsigned int gpio) { - if (event_occurred[gpio]) { - event_occurred[gpio] = 0; + if (dyn_int_array_get(&event_occurred, gpio, 0)) { + dyn_int_array_set(&event_occurred, gpio, 0, 0); return 1; } else { return 0; @@ -612,7 +624,7 @@ int blocking_wait_for_edge(unsigned int gpio, unsigned int edge) return 2; // export /sys/class/gpio interface - gpio_export(gpio); + gpio_export(gpio); // ignore errors gpio_set_direction(gpio, 0); // 0=input gpio_set_edge(gpio, edge); diff --git a/source/py_gpio.c b/source/py_gpio.c index f8af6a0..3e19d21 100644 --- a/source/py_gpio.c +++ b/source/py_gpio.c @@ -45,7 +45,7 @@ SOFTWARE. static int gpio_warnings = 1; int max_gpio = -1; -int *gpio_direction = NULL; +void *gpio_direction = NULL; struct py_callback { @@ -60,26 +60,15 @@ static struct py_callback *py_callbacks = NULL; static int init_module(void) { - int i; - - max_gpio = 1024; - gpio_direction = (int *)malloc(max_gpio * sizeof(int)); - for (i=0; i= max_gpio) { /* Does gpio_direction need to be expanded? */ - max_gpio = gpio + (gpio / 2); - gpio_direction = (int *)realloc(gpio_direction, max_gpio * sizeof(int)); - } - gpio_direction[gpio] = direction; + dyn_int_array_set(&gpio_direction, gpio, direction, -1); } // python function cleanup() @@ -124,21 +113,23 @@ static PyObject *py_setup_channel(PyObject *self, PyObject *args, PyObject *kwar return NULL; } - if (get_gpio_number(channel, &gpio)) - return NULL; - - gpio_export(gpio); - gpio_set_direction(gpio, direction); - // For some reason, the following code doesn't work for XIOs. Skip. - if (!(gpio >= lookup_gpio_by_name("XIO-P0") && gpio <= lookup_gpio_by_name("XIO-P7"))) { - if (direction == OUTPUT) { - gpio_set_value(gpio, initial); - } else { - gpio_set_value(gpio, pud); - } + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_ValueError, "Invalid value for channel"); + return NULL; } - set_gpio_direction(gpio, direction); + int result = gpio_export(gpio); + if (result < 0) { + PyErr_SetString(PyExc_ValueError, "Channel in use (already exported)"); + return NULL; + } + gpio_set_direction(gpio, direction); + // For some reason, the following code doesn't work for XIOs. Skip. + if (direction == OUTPUT) { + gpio_set_value(gpio, initial); + } + + remember_gpio_direction(gpio, direction); Py_RETURN_NONE; } @@ -153,16 +144,22 @@ static PyObject *py_output_gpio(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "si", &channel, &value)) return NULL; - if (get_gpio_number(channel, &gpio)) + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); return NULL; + } - if (!module_setup || gpio_direction[gpio] != OUTPUT) + if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != OUTPUT) { PyErr_SetString(PyExc_RuntimeError, "The GPIO channel has not been setup() as an OUTPUT"); return NULL; } - gpio_set_value(gpio, value); + int result = gpio_set_value(gpio, value); + if (result < 0) { + PyErr_SetString(PyExc_RuntimeError, "Could not write to channel"); + return NULL; + } Py_RETURN_NONE; } @@ -178,11 +175,13 @@ static PyObject *py_input_gpio(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &channel)) return NULL; - if (get_gpio_number(channel, &gpio)) + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); return NULL; + } // check channel is set up as an input or output - if (!module_setup || (gpio_direction[gpio] != INPUT && gpio_direction[gpio] != OUTPUT)) + if (!module_setup || (dyn_int_array_get(&gpio_direction, gpio, -1) == -1)) { PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel first"); return NULL; @@ -282,8 +281,10 @@ static PyObject *py_add_event_callback(PyObject *self, PyObject *args, PyObject return NULL; } - if (get_gpio_number(channel, &gpio)) - return NULL; + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); + return NULL; + } // check to ensure gpio is one of the allowed pins if (gpio != lookup_gpio_by_name("AP-EINT3") @@ -294,7 +295,7 @@ static PyObject *py_add_event_callback(PyObject *self, PyObject *args, PyObject } // check channel is set up as an input - if (!module_setup || gpio_direction[gpio] != INPUT) + if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != INPUT) { PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); return NULL; @@ -305,7 +306,6 @@ static PyObject *py_add_event_callback(PyObject *self, PyObject *args, PyObject PyErr_SetString(PyExc_RuntimeError, "Add event detection using add_event_detect first before adding a callback"); return NULL; } - if (add_py_callback(channel, gpio, bouncetime, cb_func) != 0) return NULL; @@ -331,8 +331,10 @@ static PyObject *py_add_event_detect(PyObject *self, PyObject *args, PyObject *k return NULL; } - if (get_gpio_number(channel, &gpio)) + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); return NULL; + } // check to ensure gpio is one of the allowed pins if (gpio != lookup_gpio_by_name("AP-EINT3") @@ -343,7 +345,7 @@ static PyObject *py_add_event_detect(PyObject *self, PyObject *args, PyObject *k } // check channel is set up as an input - if (!module_setup || gpio_direction[gpio] != INPUT) + if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != INPUT) { PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); return NULL; @@ -387,8 +389,10 @@ static PyObject *py_remove_event_detect(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &channel)) return NULL; - if (get_gpio_number(channel, &gpio)) - return NULL; + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); + return NULL; + } // check to ensure gpio is one of the allowed pins if (gpio != lookup_gpio_by_name("AP-EINT3") @@ -431,8 +435,10 @@ static PyObject *py_event_detected(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &channel)) return NULL; - if (get_gpio_number(channel, &gpio)) + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); return NULL; + } if (event_detected(gpio)) Py_RETURN_TRUE; @@ -451,8 +457,10 @@ static PyObject *py_wait_for_edge(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "si", &channel, &edge)) return NULL; - if (get_gpio_number(channel, &gpio)) - return NULL; + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); + return NULL; + } // check to ensure gpio is one of the allowed pins if (gpio != lookup_gpio_by_name("AP-EINT3") @@ -463,7 +471,7 @@ static PyObject *py_wait_for_edge(PyObject *self, PyObject *args) } // check channel is setup as an input - if (!module_setup || gpio_direction[gpio] != INPUT) + if (!module_setup || dyn_int_array_get(&gpio_direction, gpio, -1) != INPUT) { PyErr_SetString(PyExc_RuntimeError, "You must setup() the GPIO channel as an input first"); return NULL; @@ -507,8 +515,10 @@ static PyObject *py_gpio_function(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &channel)) return NULL; - if (get_gpio_number(channel, &gpio)) + if (get_gpio_number(channel, &gpio)) { + PyErr_SetString(PyExc_RuntimeError, "Invalid channel"); return NULL; + } if (setup_error) { @@ -538,17 +548,105 @@ static PyObject *py_setwarnings(PyObject *self, PyObject *args) // Internal unit tests extern int num_get_xio_base; +extern pins_t pins_info[]; static PyObject *py_selftest(PyObject *self, PyObject *args) { int input; if (!PyArg_ParseTuple(args, "i", &input)) return NULL; + printf("Testing get_xio_base\n"); ASSRT(num_get_xio_base == 0); int base = get_xio_base(); ASSRT(base > 0 && base < 1024); ASSRT(num_get_xio_base == 1); int second_base = get_xio_base(); ASSRT(second_base == base); ASSRT(num_get_xio_base == 1); /* make sure it didn't do the full calc a second time. */ + printf("base=%d\n", base); + + printf("Testing lookup_gpio_by_key\n"); + ASSRT(0 == lookup_gpio_by_key("U13_1")); + ASSRT(48 == lookup_gpio_by_key("U13_9")); + ASSRT(47 == lookup_gpio_by_key("U13_11")); + ASSRT(base == lookup_gpio_by_key("U14_13")); + ASSRT((base+1) == lookup_gpio_by_key("U14_14")); + ASSRT((base+6) == lookup_gpio_by_key("U14_19")); + ASSRT((base+7) == lookup_gpio_by_key("U14_20")); + ASSRT(193 == lookup_gpio_by_key("U14_23")); + ASSRT(139 == lookup_gpio_by_key("U14_38")); + ASSRT(0 == lookup_gpio_by_key("U14_40")); + ASSRT(0 == lookup_gpio_by_key("NOTFOUND")); + ASSRT(0 == lookup_gpio_by_key("U14_")); + ASSRT(0 == lookup_gpio_by_key("U14_4000")); + + printf("Testing lookup_gpio_by_name\n"); + ASSRT(0 == lookup_gpio_by_name("GND")); + ASSRT(48 == lookup_gpio_by_name("TWI1-SDA")); + ASSRT(47 == lookup_gpio_by_name("TWI1-SCK")); + ASSRT(base == lookup_gpio_by_name("XIO-P0")); + ASSRT((base+6) == lookup_gpio_by_name("XIO-P6")); + ASSRT((base+7) == lookup_gpio_by_name("XIO-P7")); + ASSRT(139 == lookup_gpio_by_name("CSID7")); + ASSRT(0 == lookup_gpio_by_name("NOTFOUND")); + ASSRT(0 == lookup_gpio_by_name("CSID")); + ASSRT(0 == lookup_gpio_by_name("CSID777")); + + printf("Testing lookup_ain_by_key\n"); + ASSRT(-1 == lookup_ain_by_key("U14_1")); + ASSRT(0 == lookup_ain_by_key("U14_11")); + ASSRT(-1 == lookup_ain_by_key("NOTFOUND")); + ASSRT(-1 == lookup_ain_by_key("U14_")); + ASSRT(-1 == lookup_ain_by_key("U14_1111")); + + printf("Testing lookup_ain_by_name\n"); + ASSRT(-1 == lookup_ain_by_name("GND")); + ASSRT(0 == lookup_ain_by_name("LRADC")); + ASSRT(-1 == lookup_ain_by_name("NOTFOUND")); + ASSRT(-1 == lookup_ain_by_name("LR")); + ASSRT(-1 == lookup_ain_by_name("LRADCCC")); + + char k[9]; + + printf("Testing copy_key_by_key\n"); + ASSRT(1 == copy_key_by_key("U13_1", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_1", k)); + ASSRT(1 == copy_key_by_key("U14_40", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U14_40", k)); + ASSRT(0 == copy_key_by_key("NOTFOUND", k)); + ASSRT(0 == copy_key_by_key("U14_", k)); + ASSRT(0 == copy_key_by_key("U14_4000", k)); + + printf("Testing copy_pwm_key_by_key\n"); + ASSRT(1 == copy_pwm_key_by_key("U13_18", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_18", k)); + ASSRT(0 == copy_pwm_key_by_key("U13_1", k)); + ASSRT(0 == copy_pwm_key_by_key("U14_40", k)); + ASSRT(0 == copy_pwm_key_by_key("NOTFOUND", k)); + ASSRT(0 == copy_pwm_key_by_key("U13_", k)); + ASSRT(0 == copy_pwm_key_by_key("U13_1888", k)); + + printf("Testing get_key_by_name\n"); + ASSRT(1 == get_key_by_name("GND", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_1", k)); + ASSRT(1 == get_key_by_name("CSID7", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U14_38", k)); + ASSRT(0 == get_key_by_name("NOTFOUND", k)); + ASSRT(0 == get_key_by_name("CSID", k)); + ASSRT(0 == get_key_by_name("CSID777", k)); + + printf("Testing get_pwm_key_by_name\n"); + ASSRT(1 == get_pwm_key_by_name("PWM0", k)); BUF2SMALL(k); ASSRT(0 == strcmp("U13_18", k)); + ASSRT(0 == get_pwm_key_by_name("NOTFOUND", k)); + ASSRT(0 == get_pwm_key_by_name("PWM", k)); + ASSRT(0 == get_pwm_key_by_name("PWM000", k)); + + char fp[80]; + + printf("Testing build_path\n"); + ASSRT(1 == build_path("/home", "ch", fp, sizeof(fp))); ASSRT(0 == strcmp("/home/chip", fp)); + ASSRT(1 == build_path("/home", "chip", fp, sizeof(fp))); ASSRT(0 == strcmp("/home/chip", fp)); + ASSRT(0 == build_path("/home", "NOTFOUND", fp, sizeof(fp))); + ASSRT(0 == build_path("/home", "chipp", fp, sizeof(fp))); + ASSRT(0 == build_path("/home", "ip", fp, sizeof(fp))); + ASSRT(0 == build_path("/NOTFOUND", "ch", fp, sizeof(fp))); + + printf("Testing get_spi_bus_path_number\n"); + ASSRT(2 == get_spi_bus_path_number(0)); /* doesn't really work on CHIP */ + ASSRT(2 == get_spi_bus_path_number(1)); /* doesn't really work on CHIP */ int value = input; diff --git a/source/py_softpwm.c b/source/py_softpwm.c index 8be3037..514c2ae 100644 --- a/source/py_softpwm.c +++ b/source/py_softpwm.c @@ -32,13 +32,13 @@ SOFTWARE. #include "Python.h" #include "constants.h" #include "common.h" -#include "c_softpwm.h" +#include "c_softpwm.h" // python function cleanup() static PyObject *py_cleanup(PyObject *self, PyObject *args) { // unexport the PWM - softpwm_cleanup(); + softpwm_cleanup(); Py_RETURN_NONE; } @@ -47,7 +47,7 @@ static PyObject *py_cleanup(PyObject *self, PyObject *args) static PyObject *py_start_channel(PyObject *self, PyObject *args, PyObject *kwargs) { char key[8]; - char *channel; + char *channel = NULL; float frequency = 2000.0; float duty_cycle = 0.0; int polarity = 0; @@ -56,6 +56,7 @@ static PyObject *py_start_channel(PyObject *self, PyObject *args, PyObject *kwar if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ffi", kwlist, &channel, &duty_cycle, &frequency, &polarity)) { return NULL; } + ASSRT(channel != NULL); if (!get_key(channel, key)) { PyErr_SetString(PyExc_ValueError, "Invalid SOFTPWM key or name."); @@ -79,7 +80,7 @@ static PyObject *py_start_channel(PyObject *self, PyObject *args, PyObject *kwar return NULL; } - if (!softpwm_start(key, duty_cycle, frequency, polarity)) + if (!softpwm_start(key, duty_cycle, frequency, polarity)) return NULL; Py_RETURN_NONE; @@ -94,12 +95,12 @@ static PyObject *py_stop_channel(PyObject *self, PyObject *args, PyObject *kwarg if (!PyArg_ParseTuple(args, "s", &channel)) return NULL; - if (!get_key(channel, key)) { + if (!get_key(channel, key)) { PyErr_SetString(PyExc_ValueError, "Invalid PWM key or name."); return NULL; } - softpwm_disable(key); + softpwm_disable(key); Py_RETURN_NONE; } @@ -121,12 +122,12 @@ static PyObject *py_set_duty_cycle(PyObject *self, PyObject *args, PyObject *kwa return NULL; } - if (!get_key(channel, key)) { + if (!get_key(channel, key)) { PyErr_SetString(PyExc_ValueError, "Invalid PWM key or name."); return NULL; } - if (softpwm_set_duty_cycle(key, duty_cycle) == -1) { + if (softpwm_set_duty_cycle(key, duty_cycle) == -1) { PyErr_SetString(PyExc_RuntimeError, "You must start() the PWM channel first"); return NULL; } @@ -145,13 +146,13 @@ static PyObject *py_set_frequency(PyObject *self, PyObject *args, PyObject *kwar if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|f", kwlist, &channel, &frequency)) return NULL; - if ((frequency <= 0.0) || (frequency > 10000.0)) + if ((frequency <= 0.0) || (frequency > 10000.0)) { - PyErr_SetString(PyExc_ValueError, "frequency must be greater than 0.0 and less than 10000.0"); + PyErr_SetString(PyExc_ValueError, "frequency must be greater than 0.0 and less than 10000.0"); return NULL; } - if (!get_key(channel, key)) { + if (!get_key(channel, key)) { PyErr_SetString(PyExc_ValueError, "Invalid PWM key or name."); return NULL; } diff --git a/test/.ropeproject/config.py b/test/.ropeproject/config.py new file mode 100644 index 0000000..3745e30 --- /dev/null +++ b/test/.ropeproject/config.py @@ -0,0 +1,96 @@ +# The default ``config.py`` + + +def set_prefs(prefs): + """This function is called before opening the project""" + + # Specify which files and folders to ignore in the project. + # Changes to ignored resources are not added to the history and + # VCSs. Also they are not returned in `Project.get_files()`. + # Note that ``?`` and ``*`` match all characters but slashes. + # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc' + # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc' + # '.svn': matches 'pkg/.svn' and all of its children + # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o' + # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o' + prefs['ignored_resources'] = [ + '*.pyc', '*~', '.ropeproject', '.hg', '.svn', '_svn', '.git', + '.tox', '.env', 'node_modules', 'bower_components'] + + # Specifies which files should be considered python files. It is + # useful when you have scripts inside your project. Only files + # ending with ``.py`` are considered to be python files by + # default. + #prefs['python_files'] = ['*.py'] + + # Custom source folders: By default rope searches the project + # for finding source folders (folders that should be searched + # for finding modules). You can add paths to that list. Note + # that rope guesses project source folders correctly most of the + # time; use this if you have any problems. + # The folders should be relative to project root and use '/' for + # separating folders regardless of the platform rope is running on. + # 'src/my_source_folder' for instance. + #prefs.add('source_folders', 'src') + + # You can extend python path for looking up modules + #prefs.add('python_path', '~/python/') + + # Should rope save object information or not. + prefs['save_objectdb'] = True + prefs['compress_objectdb'] = False + + # If `True`, rope analyzes each module when it is being saved. + prefs['automatic_soa'] = True + # The depth of calls to follow in static object analysis + prefs['soa_followed_calls'] = 0 + + # If `False` when running modules or unit tests "dynamic object + # analysis" is turned off. This makes them much faster. + prefs['perform_doa'] = True + + # Rope can check the validity of its object DB when running. + prefs['validate_objectdb'] = True + + # How many undos to hold? + prefs['max_history_items'] = 32 + + # Shows whether to save history across sessions. + prefs['save_history'] = True + prefs['compress_history'] = False + + # Set the number spaces used for indenting. According to + # :PEP:`8`, it is best to use 4 spaces. Since most of rope's + # unit-tests use 4 spaces it is more reliable, too. + prefs['indent_size'] = 4 + + # Builtin and c-extension modules that are allowed to be imported + # and inspected by rope. + prefs['extension_modules'] = [] + + # Add all standard c-extensions to extension_modules list. + prefs['import_dynload_stdmods'] = True + + # If `True` modules with syntax errors are considered to be empty. + # The default value is `False`; When `False` syntax errors raise + # `rope.base.exceptions.ModuleSyntaxError` exception. + prefs['ignore_syntax_errors'] = False + + # If `True`, rope ignores unresolvable imports. Otherwise, they + # appear in the importing namespace. + prefs['ignore_bad_imports'] = False + + # If `True`, rope will transform a comma list of imports into + # multiple separate import statements when organizing + # imports. + prefs['split_imports'] = False + + # If `True`, rope will sort imports alphabetically by module name + # instead of alphabetically by import statement, with from imports + # after normal imports. + prefs['sort_imports_alphabetically'] = False + + +def project_opened(project): + """This function is called after opening the project""" + # Do whatever you like here! diff --git a/test/.ropeproject/globalnames b/test/.ropeproject/globalnames new file mode 100644 index 0000000..02f3a4d --- /dev/null +++ b/test/.ropeproject/globalnames @@ -0,0 +1,2 @@ +}q(Utest_softpwm_setup]q(Uteardown_moduleqUTestSoftpwmSetupqeUtest_gpio_output]q(hUTestGPIOOutputqeUtest_pwm_setup]q(hU TestPwmSetupqeUtest_gpio_setup]q (hU TestSetupq +eUtest_gpio_input]q (hU TestGPIOInputq eUgptest]q (UfU loopfunctionqU num_callbacksqUedgeqUtUloopfunction_exitqU myfuncallbackqeu. \ No newline at end of file diff --git a/test/.ropeproject/history b/test/.ropeproject/history new file mode 100644 index 0000000..fcd9c96 --- /dev/null +++ b/test/.ropeproject/history @@ -0,0 +1 @@ +]q(]q]qe. \ No newline at end of file diff --git a/test/.ropeproject/objectdb b/test/.ropeproject/objectdb new file mode 100644 index 0000000..87a3774 --- /dev/null +++ b/test/.ropeproject/objectdb @@ -0,0 +1,5 @@ +}qUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py}q(U Thread._blockcrope.base.oi.memorydb +ScopeInfo +q)q}qUinstanceqUdefinedqUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyUThreadqhhUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyU +_Conditionq s}q +bUThread._set_daemonh)q }q hhUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyhhUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyU Thread.daemons}q bU currentThreadqh)q}q)hhUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyU _DummyThreadqs}qbU Conditionqh)q}qUunknownqhhUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyh s}qbUEventqh)q}q)hhUS/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyU_Eventqs}qbus. \ No newline at end of file diff --git a/test/gptest.py b/test/gptest.py index 58750a2..9e0720f 100755 --- a/test/gptest.py +++ b/test/gptest.py @@ -1,27 +1,49 @@ #!/usr/bin/python import CHIP_IO.GPIO as GPIO -import time, os, threading +import time +import threading + +num_callbacks = 0 + def myfuncallback(channel): + global num_callbacks + num_callbacks += 1 print "CALLBACK LIKE DRAKE IN HOTLINE BLING" - + + +loopfunction_exit = False + + def loopfunction(): print "LOOP FUNCTION" - for i in xrange(20): + for i in xrange(4): + if loopfunction_exit: + break if i % 2: - print "SETTING CSID0 LOW" - GPIO.output("CSID0",GPIO.LOW) + print "SETTING CSID0 LOW (i=", i, ")" + GPIO.output("CSID0", GPIO.LOW) else: - print "SETTING CSID0 HIGH" - GPIO.output("CSID0",GPIO.HIGH) + print "SETTING CSID0 HIGH (i=", i, ")" + GPIO.output("CSID0", GPIO.HIGH) print "SLEEPING" time.sleep(1) - -print "SETUP XIO-P0" -GPIO.setup("XIO-P0", GPIO.IN) -print "SETUP CSID0" + +GPIO.selftest(0) + +GPIO.setup("XIO-P0", GPIO.IN) +GPIO.setup("CSID0", GPIO.OUT, initial=GPIO.HIGH) +assert(GPIO.input("XIO-P0") == GPIO.HIGH) +GPIO.cleanup() + +GPIO.setup("XIO-P0", GPIO.IN) +GPIO.setup("CSID0", GPIO.OUT, initial=GPIO.LOW) +assert(GPIO.input("XIO-P0") == GPIO.LOW) +GPIO.cleanup() + +GPIO.setup("XIO-P0", GPIO.IN) GPIO.setup("CSID0", GPIO.OUT) # VERIFY SIMPLE FUNCTIONALITY @@ -29,51 +51,57 @@ print "VERIFY SIMPLE FUNCTIONALITY" print "READING XIO-PI" GPIO.output("CSID0", GPIO.HIGH) -print "HIGH", GPIO.input("XIO-P0") +assert(GPIO.input("XIO-P0") == GPIO.HIGH) GPIO.output("CSID0", GPIO.LOW) print "LOW", GPIO.input("XIO-P0") +assert(GPIO.input("XIO-P0") == GPIO.LOW) # ============================================== # EDGE DETECTION - AP-EINT1 print "SETTING UP EDGE DETECTION ON AP-EINT1" GPIO.setup("AP-EINT1", GPIO.IN) -GPIO.add_event_detect("AP-EINT1",GPIO.FALLING) +print "adding event detect" +GPIO.add_event_detect("AP-EINT1", GPIO.RISING) print "VERIFYING EDGE DETECT" -f = open("/sys/class/gpio/gpio193/edge","r") +f = open("/sys/class/gpio/gpio193/edge", "r") edge = f.read() f.close() -print "EDGE: %s" % edge +print "edge='", edge, "'" +assert(edge == "rising\n") GPIO.remove_event_detect("AP-EINT1") # ============================================== # EDGE DETECTION - AP-EINT3 print "SETTING UP EDGE DETECTION ON AP-EINT3" GPIO.setup("AP-EINT3", GPIO.IN) -GPIO.add_event_detect("AP-EINT3",GPIO.FALLING) +GPIO.add_event_detect("AP-EINT3", GPIO.BOTH) print "VERIFYING EDGE DETECT" -f = open("/sys/class/gpio/gpio35/edge","r") +f = open("/sys/class/gpio/gpio35/edge", "r") edge = f.read() f.close() print "EDGE: %s" % edge +assert(edge == "both\n") GPIO.remove_event_detect("AP-EINT3") # ============================================== # EDGE DETECTION - EXPANDED GPIO print "SETTING UP EDGE DETECTION ON XIO-P0" -GPIO.add_event_detect("XIO-P0",GPIO.FALLING,myfuncallback) +GPIO.add_event_detect("XIO-P0", GPIO.FALLING, myfuncallback) print "VERIFYING EDGE DETECT" -f = open("/sys/class/gpio/gpio408/edge","r") +f = open("/sys/class/gpio/gpio408/edge", "r") edge = f.read() f.close() print "EDGE: %s" % edge +assert(edge == "falling\n") # LOOP WRITING ON CSID0 TO HOPEFULLY GET CALLBACK TO WORK print "WAITING FOR CALLBACKS" loopfunction() +print "num_callbacks=", num_callbacks print "PRESS CONTROL-C TO EXIT IF SCRIPT GETS STUCK" GPIO.remove_event_detect("XIO-P0") @@ -82,18 +110,30 @@ try: t = threading.Thread(target=loopfunction) t.start() print "WAITING FOR EDGE" - GPIO.wait_for_edge("XIO-P0",GPIO.FALLING) + GPIO.wait_for_edge("XIO-P0", GPIO.FALLING) print "WE'VE FALLEN LIKE COOLIO'S CAREER" - print "ATTEMPTING TO CANCEL THE TIMER" - t.cancel() except: pass +print "Exit thread" +loopfunction_exit = True +t.join() # Wait till the thread exits. +GPIO.remove_event_detect("XIO-P0") + print "TESTING ERRORS THROWN WHEN SPECIFYING EDGE DETECTION ON UNAUTHORIZED GPIO" -GPIO.setup("CSID1",GPIO.IN) -GPIO.add_event_detect("CSID1",GPIO.FALLING,myfuncallback) +GPIO.setup("CSID1", GPIO.IN) +try: + GPIO.add_event_detect("CSID1", GPIO.FALLING, myfuncallback) + print "Oops, it did not throw an exception! BUG!!!" +except: + pass + +print "TESTING ERRORS THROWN WHEN WRITING TO A GPIO WITH NO DIRECTION" +try: + GPIO.output("CSID1", GPIO.LOW) + print "Oops, it did not throw an exception! BUG!!!" +except: + pass print "CLEANUP" -GPIO.remove_event_detect("XIO-P0") GPIO.cleanup() - diff --git a/unexport_all.sh b/unexport_all.sh new file mode 100755 index 0000000..bf497e6 --- /dev/null +++ b/unexport_all.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +for F in /sys/class/gpio/gpio[0-9]*; do : + GPIO=`echo $F | sed 's/^[^0-9]*//'` + echo $F $GPIO + echo $GPIO >/sys/class/gpio/unexport +done