qmi-codegen in libqmi-glib

Dan Williams dcbw at redhat.com
Thu May 24 09:16:28 PDT 2012


On Thu, 2012-05-24 at 09:01 +0200, Aleksander Morgado wrote:
> On 05/23/2012 10:53 PM, Dan Williams wrote:
> > On Wed, 2012-05-23 at 22:21 +0200, Aleksander Morgado wrote:
> >>>>
> >>>> I've been playing a bit more with the idea of automatically generating
> >>>> code for message handling from input JSON files, and got already
> >>>> something working in the 'qmi-codegen' branch of my libqmi-glib tree:
> >>>>   git://gitorious.org/lanedo/libqmi-glib.git
> >>>>
> >>>> The current set of supported messages are defined in the following JSON
> >>>> files:
> >>>>
> >>>> https://gitorious.org/lanedo/libqmi-glib/blobs/qmi-codegen/data/qmi-common.json
> >>>>
> >>>> https://gitorious.org/lanedo/libqmi-glib/blobs/qmi-codegen/data/qmi-service-ctl.json
> >>>>
> >>>> https://gitorious.org/lanedo/libqmi-glib/blobs/qmi-codegen/data/qmi-service-dms.json
> >>>>
> >>>> https://gitorious.org/lanedo/libqmi-glib/blobs/qmi-codegen/data/qmi-service-wds.json
> >>>>
> >>>> And adding new messages should need, ideally, just updating these JSON
> >>>> files. I guess the qmi-codegen will need updates from time to time when
> >>>> new basic types are found in the QMI messages. Currently the following
> >>>> types are supported:
> >>>>  * integers (uint8/uint16/uint32/int8/int16/int32)
> >>>>  * strings
> >>>>  * structs with integer contents
> >>>>  * arrays of structs
> >>>
> >>> At some point we'll need unions too, where the member of the union
> >>> that's actually used depends on a specific value from earlier in the
> >>> struct.  That's how stuff like "get the access technology and status"
> >>> works where you've got a big struct like:
> >>>
> >>> struct {
> >>>     bool 3gpp;  // true = 3gpp, false = 3gpp2
> >>>     union {
> >>>         struct {
> >>>             uint mcc;
> >>>             uint mnc;
> >>>             uint lac;
> >>>             uint cid;
> >>>             enum rat_type;
> >>>         } 3gpp;
> >>>         struct {
> >>>             bool is_1x_or_hdr;
> >>>             union {
> >>>                 struct {
> >>>                     1x junk;
> >>>                 } 1x;
> >>>                 struct {
> >>>                     evdo junk;
> >>>                 } hdr;
> >>>             } u2;
> >>>         } 3gpp2;
> >>>     } u;
> >>> }
> >>>
> >>> that's some evil stuff, but basically  normal protocol packet parsing :(
> >>> Anyway, I do like the idea of doing this with JSON.  I despair of
> >>> actually being able to use the Gobi database except for reference since
> >>> generating the code out of them is horrendously complicated.
> >>
> >>
> >> I'll check that message during the next days and also try to provide
> >> codegen for it.
> >>
> >> Still have other fixes in mind for it, but at least as it is now it
> >> should be equivalent feature-wise to the master branch.
> > 
> > eg sWDSEventReportIndication_DataSystems in
> > GobiConnectionMgmtAPIStructs.h.  That's basically a worst-case scenario,
> > but unfortunately one that is kinda important.  The bitfields and
> > padding there just make it worse, though ideally we can reduce the
> > bitfields to flag enums or something.  That struct isn't quite as bad as
> > it looks, since in reality all of sNetworkTypeIs1 can be represented by:
> > 
> > enum CdmaType {
> >     /* 1x, evdo0, evdoA, and evdoB are mutually exclusive,
> >      * but eHRPD could be set when any of evdo0, evdoA,
> >      * or evdoB are set.
> >      */
> >     1x = 0x01;
> >     evdo0 = 0x02;
> >     evdoA = 0x04;
> >     evdoB = 0x08;
> >     eHRPD = 0x10;
> > };
> > 
> > enum Cdma1xInfo {
> >     Cdma1x_is95 = 0x01;
> >     Cdma1x_is2000 = 0x02;
> >     Cdma1x_is2000RelA = 0x04;
> > };
> > 
> > enum Evdo0Info {
> >     Evdo0_dpa = 0x01;
> > };
> > 
> > enum EvdoAInfo {
> >     EvdoA_dpa = 0x01;
> >     EvdoA_mfpa = 0x02;
> >     EvdoA_empa = 0x04;
> >     EvdoA_empa_ehrpd = 0x08;
> > };
> > 
> > enum EvdoBInfo {
> >     EvdoB_dpa = 0x01;
> >     EvdoB_mfpa = 0x02;
> >     EvdoB_empa = 0x04;
> >     EvdoB_empa_ehrpd = 0x08;
> >     EvdoB_mmpa_ehrpd = 0x10;
> >     EvdoB_mmpa_ehrpd = 0x20;
> > };
> > 
> > struct sNetworkTypeIs1 {
> >     enum:uint8 CdmaType cdma_type;
> >     uint8 padding1[2];
> >     uint8 null_bearer; // 0x80 = TRUE, 0x0 = FALSE
> >     union {
> >         enum:uint8 Cdma1xInfo 1xinfo;
> >         enum:uint8 Evdo0Info  e0info;
> >         enum:uint8 EvdoAInfo  eAinfo;
> >         enum:uint8 EvdoBInfo  eBinfo;
> >     };
> >     uint8 padding2[3];
> > }; 
> > 
> 
> I already handled something like that in the current code, the result
> for the "Get Current Data Bearer Technology". Instead of the real enums
> being specified in the last union, I just used a generic guint32
> 'so_mask'. Then, each enum/flags defined has proper documentation on
> where to use it, like:
> 
> /**
>  * QmiWdsSoCdma1x:
>  * @QMI_WDS_SO_CDMA1X_NONE: Unknown, to be ignored.
>  * @QMI_WDS_SO_CDMA1X_IS95: IS95.
>  * @QMI_WDS_SO_CDMA1X_IS2000: IS2000.
>  * @QMI_WDS_SO_CDMA1X_IS2000_REL_A: IS2000 RelA.
>  *
>  * Flags specifying the Service Option when the bearer network type is
>  * @QMI_WDS_NETWORK_TYPE_3GPP2 and when the Radio Access Technology mask
>  * contains @QMI_WDS_RAT_3GPP2_CDMA1X.
>  */
> typedef enum {
>     QMI_WDS_SO_CDMA1X_NONE         = 0,
>     QMI_WDS_SO_CDMA1X_IS95         = 1 << 0,
>     QMI_WDS_SO_CDMA1X_IS2000       = 1 << 1,
>     QMI_WDS_SO_CDMA1X_IS2000_REL_A = 1 << 2
> } QmiWdsSoCdma1x;
> 
> The user of the library *needs* to know when the so_mask contains a
> specific set of flags. The user needs to know it both in this case,
> where you get a generic mask, in order to know which set of flags to
> use; but it also needs to know it if we provide a union of different
> fields, in order to know which field of the union is the appropriate
> one. Aren't then the unions (at least in this case) just an additional
> complication that we can just completely skip?

It depends on how complex the structures inside the union are.  In this
case, the RAT-specific flags (*Info in my example) fit nicely together
and don't overflow a uint8.  But I'm not sure about others.  If it's a
more complex structure, obviously there are two ways to go about parsing
it: use unions and access the members directly, or start casting void*
to the type you want.  Unions make the code a bit nicer, at least until
you start getting 2 or 3 unions deep like some of this stuff.  At that
point I've found casting is usually clearer.  I've worked with a lot of
firmware protocol parsing stuff for kernel wifi drivers, but the
protocols there were never as complicated as QMI.

Dan



More information about the libqmi-devel mailing list