Hello; I've been tring to get the knack of the posix API. The attached code is influenced by "Frank" example. The idea is to make a four channel pwm generator to drive motors hung off the port. One thread, pwm_assert, begins the pwm period for all channels by asserting the apropriate lines to the apropriate levels. Four other threads, pwm_negate[0-3], handle ending the active part of the cycle for each channel. Real time fifos 0-3 accept signed char values for magnitude and direction of drive for channels 0-3. The only trick is magnitudes of 127 and -128 suspend the channel's pwm_negate thread (at max drive, I do not want the duty cycle to end).
Testing with rtl_printk showed the fifos processed writes correctly and the correct bit patterns were being generated and written to the printer port. Where this all seems to fall down is after addition of the code to suspend a thread. Sporadic hard lockups when writing from user space to the fifos (usually 3, but not allways) and sometimes during rmmod. Again, the posix API is still new so I may not be grokking where and how to use mutex locks or thread suspention/wake_up. There is no contention for the io space of the printer port, parport_pc and parport modules are unloaded. Uniprocessor AMD-K2, kernel 2.4.4-rtl with no other patches, rtl, rtl-time, rtl-sched, rtl-posixio and rtl-fifo loaded. Any help, advice or research direction would be greatly appreciated. -R. -- GPG-fingerprint: 6CE9 8B6E 8089 FECC 4AA6 9CC9 F2B3 70A6 E98A 8B77
#include <rtl.h>
#include <time.h>
#include <pthread.h>
#include <rtl_sched.h>
#include <rtl_fifo.h>
#define PERIOD 2000000 /* 500 Hz */
static int ioport = 0x378;
static pthread_t pwm_assert_task;
static pthread_t pwm_negate_task[4];
static char pwm_negate_task_last_setting[4];
static int pwm_negate_task_suspended[4];
static pthread_mutex_t port_current_pattern_mutex;
static char port_current_pattern;
static pthread_mutex_t port_starting_pattern_mutex;
static char port_starting_pattern;
static hrtime_t current_period_begin;
void *pwm_assert(void *thread_parm) {
char port_channel_pattern;
current_period_begin = gethrtime();
while (1) {
pthread_mutex_lock(&port_current_pattern_mutex);
pthread_mutex_lock(&port_starting_pattern_mutex);
port_channel_pattern = port_current_pattern = port_starting_pattern;
pthread_mutex_unlock(&port_starting_pattern_mutex);
pthread_mutex_unlock(&port_current_pattern_mutex);
rtl_outb(ioport, port_channel_pattern);
pthread_wait_np();
}
}
void *pwm_negate(void *thread_parm) {
int channel = (int)thread_parm;
char port_channel_pattern;
while (1) {
if (pwm_negate_task_suspended[channel]) {
pthread_suspend_np(pthread_self());
}
pthread_mutex_lock(&port_current_pattern_mutex);
port_channel_pattern = port_current_pattern &= ~(char)(0x03 << 2 * channel);
pthread_mutex_unlock(&port_current_pattern_mutex);
rtl_outb(ioport, port_channel_pattern);
pthread_wait_np();
}
}
int pwm_setting_handler(unsigned int fifo) {
char pwm_setting;
char port_channel_pattern;
hrtime_t pwm_active_period;
rtf_get(fifo, &pwm_setting, sizeof(pwm_setting));
if (pwm_setting != pwm_negate_task_last_setting[fifo]) {
if (pwm_setting == 0) {
port_channel_pattern = 0x00;
pwm_active_period = 0;
}
else {
if (pwm_setting < 0) {
port_channel_pattern = 0x02;
pwm_active_period = -pwm_setting * PERIOD / 128;
}
else {
port_channel_pattern = 0x01;
pwm_active_period = pwm_setting * PERIOD / 128;
}
}
pthread_mutex_lock(&port_starting_pattern_mutex);
port_starting_pattern &= ~(char)(0x03 << 2 * fifo); /* clear channel bitfield */
port_starting_pattern |= (char)(port_channel_pattern << 2 * fifo); /* set desired bits in channel bitfield */
pthread_mutex_unlock(&port_starting_pattern_mutex);
if (pwm_setting == 127 || pwm_setting == -128) {
if (! pwm_negate_task_suspended[fifo]) {
pwm_negate_task_suspended[fifo] = 1;
}
}
else {
if (pwm_negate_task_suspended[fifo]) {
pwm_negate_task_suspended[fifo] = 0;
pthread_wakeup_np(pwm_negate_task[fifo]);
}
}
pthread_make_periodic_np(pwm_negate_task[fifo], current_period_begin + pwm_active_period, PERIOD);
pwm_negate_task_last_setting[fifo] = pwm_setting;
}
return 0;
}
int init_module(void) {
pthread_attr_t attr;
struct sched_param sched_param;
int i;
port_current_pattern = 0x00;
port_starting_pattern = 0x00;
rtf_destroy(0);
rtf_destroy(1);
rtf_destroy(2);
rtf_destroy(3);
rtf_create(0, 200);
rtf_create(1, 200);
rtf_create(2, 200);
rtf_create(3, 200);
pthread_attr_init(&attr);
sched_param.sched_priority = 4;
pthread_attr_setschedparam(&attr, &sched_param);
pthread_create(&pwm_assert_task, &attr, pwm_assert, NULL);
current_period_begin = gethrtime();
pthread_make_periodic_np(pwm_assert_task, current_period_begin, PERIOD);
pthread_attr_init(&attr);
sched_param.sched_priority = 5;
pthread_attr_setschedparam(&attr, &sched_param);
for (i = 0; i < 4; i++) {
pthread_create(&pwm_negate_task[i], &attr, pwm_negate, (void *)i);
pwm_negate_task_suspended[i] = 0;
pwm_negate_task_last_setting[i] = 0;
pthread_make_periodic_np(pwm_negate_task[i], current_period_begin, PERIOD);
rtf_create_handler(i, &pwm_setting_handler);
}
return 0;
}
void cleanup_module(void) {
int i;
rtf_destroy(0);
rtf_destroy(1);
rtf_destroy(2);
rtf_destroy(3);
pthread_cancel(pwm_assert_task);
pthread_join(pwm_assert_task, NULL);
for (i = 0; i < 4; i++) {
pthread_cancel(pwm_negate_task[i]);
pthread_join(pwm_negate_task[i], NULL);
}
}
msg02139/pgp00000.pgp
Description: PGP signature
