Andrew Thomas wrote:
The problem I have, whether real or imagined (I’m still learning),
is that not all devices offer meaningful interpretations of open,
close, read, write, poll, etc. For example, I want to write a driver
to an Allen-Bradley KT card for Data Highway Plus. Look at the calls:
open - makes sense
close - makes sense
OK so far.
write - useless for synchronous messaging
read - similarly useless
Reasonable statement.
poll - similarly useless
Select/Poll, is generally useful for things like control networks (which I
assume AB Data Highway is).
flush - meaningless
etc.
OK.
and, now I have a pile of devctl()s to implement…
read block
write block
read/mask/write bits
open processor
change processor mode
query processor mode
…
etc (there are many).
These don’t need to be devctls. MsgSend()/Sendfd() will do nicely for
these, if portability is not an issue.
You know, I wrote a resmgr for a control network once. In prefix namespace it
looked like this.
/dev/ctrlnet/processor1/mem
/proc
/task1
/task2
/processor2/mem
/proc
/task1
Where ctrlnet is a particular network (there could be multiple)
processorX is a particular node (PLC what have you)
taskX is a particular task on a particular controller.
You could:
“spatch /dev/ctrlnet/processor1/mem” to view the controllers physical memory.
“ls /dev/ctrlnet/processor2/proc” to view the tasks running on processor2.
“rm /dev/ctrlnet/processor1/proc/task2” to terminate task2 on processor1.
“cp /src/foo.hex /dev/ctrlnet/processor1/proc” to download, and begin execution
of a task on processor1.
I could use Photon filemanager to download and begin execution of a task on
processors on the network.
I could use Photon filemanager to terminate tasks on processors on the network.
I could write a 5 line shell script to shutdown/startup every task on the
network.
A programmer could open("/dev/ctrlnet/processor1/proc/task1", O_RDWR), and then
(using the returned fd) “Sendfd(fd, …)” to send messages to a task (exactly
the same way one would do the same thing for a QNX process). Of course, the
messages to a task would be proprietary, but look at all the infrastructure that
was provided by the standard API. Also select(fd,…) would work, and a client
program could wait on a message from multiple tasks on multiple controllers in
the network; while simultaneously waiting for keyboard input or Photon events,
or; yada, yada, yada.
Anyway, for me it certainly seemed like I could successfully overload a whole
whack of standard operations for a control network.
What have I gained from using the POSIX model? I get told when the
client exits,
This is good, I think; 1 point.
even if it crashes. I get to publish a name in the file
system name space.
This is also good; 2 points (btw: I prefer the term prefix name space).
I can use standard utilities to perform some
opeations.
Again this is good; 3 points.
I’m just not sure if it’s worth the complexity of the
POSIX wrapper for these benefits.
Hmmm, have you played with neutrino much ? It really isn’t very complex to do a
resmgr.
Now to apply the same criteria to creating a completely non-standard interface.
OK, I have created a proprietary interface; what have I gained ? I saved
perhaps 50 lines of code, over doing a resmgr. I have to do my own name
location mechanism (which no other program/utility will understand), I have to
handle resource reclamation when a client dies (I’m not sure if it is even
possible to reliably do this without handling a close in Neutrino; in QNX 4 it
is doable but ugly). If my driver is going to be multi-threaded I will also have
to do my own thread pool management.
For example, using the POSIX mechanism (assuming you don’t have a
/proc filesystem), people tend to limit the information available.
Look at mqueue. You can use ls -l to look at the queue sizes, but
what about information like number of readers, wait states of readers,
number of writers, wait states of writers, maximum messages in queue,
total bytes queued, queue owner? ls -l won’t give me these, so in
order to get this info, I need another devctl() and a custom tool to
call it.
If the original author of mqueue had been working with a
custom interface, he (or she) might have thought to offer this
information.
I don’t buy the argument that because a developer is using a standard interface,
that they subconsciously limit themselves to the functionality that the
interface can provide. Is this what you are saying ?
I know that the POSIX mechanism offers endless
flexibility through devctl(), which is really just a way to create a
custom API, but programmers who already give us some information
through standard tools tend not to go to the trouble of making the
custom tools to do the rest of the job.
You don’t need to create devctls under QNX (for custom API’s); however, if you
want portability it isn’t a bad idea to do so. Consider that if you build all
of your custom API over devctl’s then it will be no more difficult to port your
driver to another Posix system; and all of your clients will compile and link
first time (assuming that all other aspects are Posix).
I’m not trying to slag the POSIX model. I think it’s a Good Thing.
But I do agree with Mitchell that it may not always fit.
I don’t remember disagreeing with Mitchell. I simply made the point that when a
device driver can reasonably be placed in the Posix interface model, it is more
than just a matter of taste whether you do so or not (there are real
quantifiable differences in the utility of the end result).
Andrew Thomas, President, Cogent Real-Time Systems Inc.
2430 Meadowpine Boulevard, Suite 105, Mississauga, Ontario, Canada L5N 6S2
Email: > andrew@cogent.ca > WWW: > http://www.cogent.ca
–
To succeed in this world it is not enough to be stupid; you must also be
well-mannered. - Voltaire