Oliver, 

On Mon, Feb 15, 2010 at 11:03:09AM +0100, Oliver Hartkopp wrote:
> Kurt Van Dijck wrote:
> > On Sat, Feb 13, 2010 at 05:32:52PM +0100, Oliver Hartkopp wrote:

> Bloch, J. How to Design a Good API and Why it Matters. Oct. 2005.
> http://lcsd05.cs.tamu.edu/slides/keynote.pdf
I did go through it (mainly skipped the 'class design')
> 
I'll try to summarize the core points of what we are implementing now:
knowing my communication skills, I'll forget some :-).

* j1939 divides the can_id into several fields.
  dp, reserved, pf serve the same purpose, and are grouped into 'pgn'
  sa is a seperate field
  ps is special one: it can be destination address, or it can be part of
  the pgn.
  priority (3bits) is to be ignored for incoming messages.
* a struct sockaddr_can (I attached before) with all 'header' info,
   so the packet data is the only data.
  necessary members: sa(8), da(8), pgn(18), priority(3)
* bind() will assign local address.
* connect() assigns destination address.
* recvfrom() will put all info in the sockaddr_can.
* sendto() will use the sockaddr_can for header info (SA, DA, PGN, ...)

* bind, connect & sendto all influence the final outgoing packet. IMO,
   it is as designed with regard to the socket API.

* We want to extend the j1939 bus into the
  linux kernel, so different sockets can be logically different
  entities, just as can_raw does for can.
  A major point of attention is that we also want to be able to have
  different sockets cooperate to form 1 logical entity on the bus, where
  a logical entity means '1 source address'.
  This way, we can seperate common funcionalities, found in many logical
  entities (such as error reporting, version reporting) and
  core functionality in different processes.
  Therefore, bind allows to share the same SA.

* A filtering similar to CAN is applied, but on PGN & SA level.

Up to here, we need a layout similar to:
struct sockaddr_can {
   ...
   union {
      struct {
         uint8_t sa, da;
         uint8_t priority;
         uint32_t pgn;
      } j1939;
   } can_addr;
};

where not all fields are necessarily used in all api calls.
note that this does go further than only implementing the transport
protocol, but the transport protocols do not need extensions for their
own! In our current implementation, just doing sendto() with more than 8
bytes data result in a transport protocol session setup, transparently.
Note that the transport protocol in j1939 uses 2 pgns, but such thing
should still be described with (SA, DA, PGN, PRIORITY).

After reading the relevant man pages & looking at the sockaddr_can
above, the only debate I see is wether to set SA from connect(), and DA
from bind(). That the only documenting up to here.

When you say 'redundant parts', I can suggest
that sa & da do not necessarily be in the same structure. uint8_t addr
would be sufficient too. in bind(), it means SA, in connect() it means
DA, etc, etc.
For getting the precise DA in recvfrom, a getsockopt could be used
(similar to timestamps).
struct sockaddr_can {
   ...
   union {
      struct {
         uint8_t addr;
         uint8_t priority;
         uint32_t pgn;
      } j1939;
   } can_addr;
};

At the next level, j1939 describes a dynamic addressing scheme (the
above is working fine with static addresses only). That's especially
used a lot in agriculture (our main market).
At that point, SA & DA are not persistent anymore, but the 8byte name
is.
We intent not to let kernel code participate in this dynamic addressing,
but let the kernel code follow the actions on the bus. Since a single
instance must decide which name an SA belongs to & vice versa, the
kernel is the perfect place.
To participate in dynamic addressing, userspace code is needed.
But userspace programs cooperating to a single entity on the bus, must
not both participate in managing dynamic addresses. That's where the
uint64_t came up in the sockaddr_can. This name is used by kernel code
to find the current SA according to the 8byte name.
This setup results in 1 extra process managing the SA of a particular 8byte
name, and all others operating on that name level.

the sa & da members in the structure are therefore rarely used in
transmissions from userspace, but keep being relevant to track the
dynamic addressing on the bus.
Setting the 8byte name via setsockopt would violate the explanation for
bind(), which _sets the local address_, and corrupts the use of
recvfrom(), as the SA could have changed in the meanwhile.
Therefore, we found 'uint64_t name' being a member of sockaddr_can, just
for sake of reusing existing API's.
Generally speaking, the use of 8byte name overrides the use of 1byte SA.
Since 0 is an invalid name, it would mean to use the 1byte SA (and no
      dynamic addressing).
struct sockaddr_can {
   ...
   union {
      struct {
         uint64_t name;
         uint8_t addr;
         uint8_t priority;
         uint32_t pgn;
      } j1939;
   } can_addr;
};


Now, I think this looks long enough for first iteration :-)
I'm interested to see what could get stripped.

Kurt
_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to