I have a requirement to transmit data and receive data at the same
time. The received data is not done during the read(). It is performed
during the write().
the user calls the ioctl first specifying a buffer for the receive
buffer. Then the write() is performed as follows:
getpid of the write task.
getpriority() of write task,
stsetprio() of the interrupt thread ID to (stgetprio() <<1)+1.
setup output dma using the following steps:
mem_lock() /*locks users' output buffer using write task id*/
caculate number of DMA pages required for operation.
mmchain() to retrieve physical address of output DMA pages.
setup receive dma using the following steps:
mem_lock() /*locks users' output buffer using write task id*/
caculate number of DMA pages required for operation.
mmchain() to retrieve physical address of received buffer DMA pages.
Setup hardware for output and input DMA.
Enable the hardward for DMA operation.
when interrupt occurs, mem_unlock() both output and input buffers.
The above operation works find the first time. However, if I try it
again, the system either crashes(most of the time) or hangs up.
If I do strictly write() operations, it works find...no crashes.
It looks like the receive buffer is causing a problem but I haven't
been able to figure it out. It has been 3 days, and I need to solve
this problem.
I noticed that I'm using the write task ID to lock the memory for
receive buffer although the buffer was give to the driver during an
ioctl call. Is this correct? Could this be causing my problem? should
I be locking the received buffer during the ioctl call?
My experience with LynxOS is limitted. I'm a Windows Device Driver
guy.
Any suggestion?
> If I do strictly write() operations, it works find...no crashes.
"Works fine" meaning it doesn't crash or doesn't crash AND the written data is
correct (as in matches what was expected to be written)?
Correct. The write() completes and the data is received correctly.
However, if I try to receive anything, the system crashes.
That means virtual to bus address conversion is done properly... Hard to say
what's wrong with the read, then. Can you quote your code that converts virtual
addresses to bus addresses?
Here's the code, common to both the write buffer and receive buffer.
The taskId are the same for the write and the receive.
/* Lock memory */
if (mem_lock(taskId, Buffer, Length))
{
pseterr(ENOMEM);
return (SYSERR);
}
pages = ((Length + PAGESIZE - 1) / PAGESIZE) + 1;
os_array = os_memory_alloc(sizeof(struct dmachain) * (pages + 1));
DmaArray = os_memory_alloc(sizeof(DMA_ARRAY) * (pages + 1));
/* get memory page information */
num = mmchain(os_array, Buffer, Length);
for (i =0; i < num; i++)
{
DmaArray[i].buffer = os_array[i].address - PHYSBASE + drambase;
DmaArray[i].buffer += os_info->address_modifier;
DmaArray[i].nbytes = os_array[i].count;
DmaArray[i].nwords = os_array[i].count >> 2;
}
/* Null out last entry */
DmaArray[i].buffer = 0;
DmaArray[i].nbytes = 0;
DmaArray[i].nwords = 0;
The DmaArray is saved in the driver's data structure. One for write
and one for received.
If I do the write without the receive, everthing works ok. If I do a
read(). That works ok. The problem shows up when I try the write()
with a receive at the same time (no read()). The first time it appears
to work find. But when I try to do any I/O read() or write(), the
system crashes or hangs-up.
I'm running out of ideas.
I do not see any problems in the code.
I have the following notes:
> when interrupt occurs, mem_unlock() both output and input buffers.
Hopefully, you don't call mem_unlock() from the interrupt handler. Make sure you
don't make any calls that are not explicitly allowed from interrupt handlers.
> I noticed that I'm using the write task ID to lock the memory for
> receive buffer although the buffer was give to the driver during an
> ioctl call. Is this correct? Could this be causing my problem? should
> I be locking the received buffer during the ioctl call?
Where does the buffer memory come from? If from the userland, you should lock it
during ioctl(). Hopefully, the process that allocated the buffer is still alive
when you do DMA. If the buffer is allocated with sysbrk(), you don't need to
lock it at all, kernel memory is not swappable. In fact, something may break if
you attempt to lock it (though I am not familiar with that LynxOS version code
that well).
The buffer does come from the user during ioctl() but I don't lock it
down until the write(). The process is still active. the application
calls the ioctl() to set the receive buffer and other stuff and then
calls the write() to kick thing off.
the wierd part is that everything looks ok from the print statements
and bus analyzer. The driver gets back to the user and then the system
crashes. It does sound like a pointer problem but I just don't see it.
the code is common to other drivers on other systems and they work
just find.
It sure sucks to be me right now.
>>> when interrupt occurs, mem_unlock() both output and input buffers.
>> Hopefully, you don't call mem_unlock() from the interrupt handler. Make sure you
>> don't make any calls that are not explicitly allowed from interrupt handlers.
>>
>>> I noticed that I'm using the write task ID to lock the memory for
>>> receive buffer although the buffer was give to the driver during an
>>> ioctl call. Is this correct? Could this be causing my problem? should
>>> I be locking the received buffer during the ioctl call?
>> Where does the buffer memory come from? If from the userland, you should lock it
>> during ioctl(). Hopefully, the process that allocated the buffer is still alive
>> when you do DMA. If the buffer is allocated with sysbrk(), you don't need to
>> lock it at all, kernel memory is not swappable. In fact, something may break if
>> you attempt to lock it (though I am not familiar with that LynxOS version code
>> that well).
>
> The buffer does come from the user during ioctl() but I don't lock it
> down until the write(). The process is still active. the application
> calls the ioctl() to set the receive buffer and other stuff and then
> calls the write() to kick thing off.
So, are ioctl() and write() called by the same process or not? I'm confused by
another post where you said:
"I noticed that I'm using the write task ID to lock the memory for
receive buffer although the buffer was give to the driver during an
ioctl call. Is this correct?"
Are you asking whether a "task ID" (I assume you talk about the process ID here)
is the same during a process' lifetime? Yes, the process ID is constant.
Yes, it is called by the same process. The process first calls the
ioctl() to setup the receive buffer and then calls the write() to
kick-off the write/read opeartion.
The read is independent of the write operation. The read acts like an
echo. for every word transmitted, the same word is received. I only
get 1 interrupt, on the completion of the output. I'm going to try to
put a delay on the write completion to ensure the read buffer has not
been unlocked and paged out. I don't think it is. The bus analyzer
shows that interrupt thread is waking up to process the interrupt in
approx. 100 ms, which is more that enough time for the last word to be
received into memory before the memory is unlock and maybe paged out.
My next step is to use kernel memory, receive the data, and then copy
the data back to the user.
If this works, I might have to do that way although I personally don't
like it. time is running out.