Hi,
I seem to be having problems passing data from the Kernel to RTLinux. My
system is 2.2.18 and rtlinux 3.0-final. The requirements are that an app
can pass a block of data to the kernel driver. The kernel driver will then
pass this in to a shared memory space (buffer) which can be accessed by
RTLinux. When the buffer is full the application should sleep allowing
other stuff to run. When the buffer empties the RTLinux thread should sleep
and wait to be given new data.
The data received by RTLinux contains delays (min 1 uSec although these
don't occur often) and hardware register writes. Compensation code has been
added to steal time from other bigger delays should things begin to slip.
So far I've implemented a system in RTLinux and it works extreamely well,
but we seem to have a problem. The shared resource is accessed through
semaphores and this mechanism seems to be the ideal way. Double buffering
of the data dosen't seem to be possible due to undetermined delay lengths
which at times can exceed 65ms (timing is very varied).
I used the provided sem_t semaphores of rtlinux and running the code
everything seemed to be great (using standard Linux semaphores seemed to
kill the machine used in the real time thread). On my slow PC the load was
high but didn't appear excessive for what it was doing. My friend then
tried it on his Athlon 800 to find the load to be 100%... Even when we
generated lots of data at very large periods it still read 100 (these should
have effectively caused the app to sleep and the real time thread to
schedule large delays). My guess is that the sem_t has caused the whole of
Linux to sleep rather than just the Linux app. So on thinking about it a
bit more I decided on a mixed rtlinux/linux semaphore scheme. This worked
great, but when the delays were reduced, the system died after running for a
while. A segmentation fault was generated stating that there was a NULL
pointer dereference in the Kernel.
So the question is is there a better way to pass the data between the Kernel
and real time threads? Preferabaly I do not want to upgrade to Kernel 2.4.0
as I have extra Kernel modules which aren't ready for it yet. Also I don't
want to use the fifos because I want it to be a contained driver providing
it's own ioctls.
Thankyou for any help,
Simon
Most Important functions:
/* We use hsid_lock to protect against concurrent opens. So the BKL is not
* needed here. Or anywhere else in this driver. */
static int hsid_open(struct inode *inode, struct file *file)
{
sid_d *sid;
sem_t sem;
sem_init (&sem, 0, 0);
spin_lock(&hsid_lock);
/* Check for bad node */
if ( (inode->i_rdev & 0xff) >= sid_numSIDs )
goto out_busy;
/* Device already open */
sid = sid_data[inode->i_rdev & 0xff];
if(sid->status & HSID_IS_OPEN)
goto out_busy;
sid->status |= HSID_IS_OPEN;
sid->curCommand = 0;
sid->lastCommand = 0;
#ifdef KERNEL_2_2
init_MUTEX(&sid->bufferSem);
#endif
sema_init (&sid->bufferSem, HSID_BUFFER_SIZE);
sem_init (&sid->todoSem, 0, 0);
if (!sid_open)
{
notify = &sem;
pthread_create (&thread, NULL, hsid_thread, 0);
sem_wait(&sem);
printk ("<1>Started\n");
}
sid_open++;
spin_unlock(&hsid_lock);
return 0;
out_busy:
spin_unlock(&hsid_lock);
return -EBUSY;
}
static ssize_t hsid_write(struct file * file, const char * buffer,
size_t count, loff_t *ppos)
{
int ret = 0;
size_t bytes;
__u32 buf;
const char *p = buffer;
size_t c = count;
sid_d* sid;
if ( GETMINOR(file) >= sid_numSIDs )
return -EFAULT;
sid = sid_data[GETMINOR(file)];
while (c > 0)
{
bytes = MIN(c, sizeof(buf));
bytes -= copy_from_user(&buf, p, bytes);
if (!bytes)
{
ret = -EFAULT;
break;
}
c -= bytes;
p += bytes;
/* We want multiples of 4 bytes here */
if ( bytes != 4 )
break;
/* Command structure:
bits 31-16: 16 bit delay timer value in C64 clock cycles (1uSec)
bits 15-13: reserved, keep zero
bits 12-8: SID register number
bits 7-0: Data
*/
down (&sid->bufferSem);
sid->buffer[sid->lastCommand++] = buf;
sid->lastCommand &= HSID_BUFFER_SIZE - 1;
sem_post (&sid->todoSem);
sem_post (&todoSem);
}
if (p == buffer)
{
return (ssize_t)ret;
}
else
{
file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
mark_inode_dirty(file->f_dentry->d_inode);
return (ssize_t)(p - buffer);
}
}
/* The main worker thread (Real Time) */
void *hsid_thread(void* data)
{
sid_d *sid;
__u32 cmd;
struct sched_param p;
hrtime_t curr_time, last_time;
long delta, delay = 0;
struct timespec t;
pthread_t self;
p.sched_priority = 1;
pthread_setschedparam (pthread_self (), SCHED_FIFO, &p);
active = 1;
sem_post (notify);
printk ("<1> Notfied\n");
self = pthread_self ();
last_time = gethrtime ();
t.tv_sec = 0;
for(;;)
{
sem_wait (&todoSem);
sid = sid_data[0]; // Hard coded to minor 0 for testing
sem_wait (&sid->todoSem);
cmd = sid->buffer[sid->curCommand++];
sid->curCommand &= HSID_BUFFER_SIZE - 1;
delay += (cmd >> 16) * 1000;
if ((cmd & 0x1f00) == 0x1f00)
{
up (&sid->bufferSem);
continue;
}
delta = delay;
curr_time = gethrtime ();
delta -= (long) (curr_time - last_time);
last_time += delay;
if (!delay && delta > -1000)
delta += 1000;
if (delta > 1000)
{
t.tv_nsec = delta;
nanosleep (&t, NULL);
} // Give up if we have lost 100 uSecs or more
else if (delta < -100000)
last_time = curr_time;
delay = 0;
spin_lock(&hsid_reg_lock);
outw ((cmd & 0x1fff) | (sid->chip << 14), sid->port);
spin_unlock(&hsid_reg_lock);
up (&sid->bufferSem);
}
/* Off we go */
active = 0;
return 0;
}
NOTE: Using:
RTL_MARK_SUSPENDED (self);
pthread_make_periodic_np (....); // With period 0
or
pthread_make_periodic_np (....); // With period 0
pthread_wait_np ();
Instead of nanosleep/usleep will lock up the machine for small delays when
the system becomes loaded through another program running in the
background....?
-- [rtl] ---
To unsubscribe:
echo "unsubscribe rtl" | mail [EMAIL PROTECTED] OR
echo "unsubscribe rtl <Your_email>" | mail [EMAIL PROTECTED]
--
For more information on Real-Time Linux see:
http://www.rtlinux.org/rtlinux/