Why no binary reuse?

nsaspook

Joined Aug 27, 2009
13,273
It's the same principle with most Linux drivers.

https://raw.githubusercontent.com/nsaspook/daq_gert/master/daq_gert.c

C:
    if (devpriv->num_subdev > 1) { /* setup comedi for on-board devices */
        /* daq_gert ai */
        if (devpriv->use_hunking)
            dev_info(dev->class_dev,
            "hunk ai transfers enabled, length: %i\n",
            hunk_len);
        s = &dev->subdevices[1];
        s->private = devpriv->ai_spi;
        s->type = COMEDI_SUBD_AI;
        /* default setups, we support single-ended (ground)  */
        s->n_chan = devpriv->ai_spi->chan;
        s->len_chanlist = devpriv->ai_spi->chan;
        s->maxdata = (1 << (thisboard->n_aichan_bits - devpriv->ai_spi->device_spi->n_chan_bits)) - 1;
        if (devpriv->ai_spi->range)
            s->range_table = &daqgert_ai_range2_048;
        else
            s->range_table = &daqgert_ai_range3_300;
        s->insn_read = daqgert_ai_rinsn;
        if (devpriv->smp) {
            s->subdev_flags = devpriv->ai_spi->device_spi->ai_subdev_flags;
            s->do_cmdtest = daqgert_ai_cmdtest;
            s->do_cmd = daqgert_ai_cmd;
            s->poll = daqgert_ai_poll;
            s->cancel = daqgert_ai_cancel;
        } else {
            s->subdev_flags = devpriv->ai_spi->device_spi->ai_subdev_flags - SDF_CMD_READ;
        }
        if (devpriv->ai_spi->device_type == ads1220) {
            /* we support single-ended (ground) & diff bipolar  24-bit samples */
            /* 32 bit buffers */
            lsamp_size = SDF_LSAMPL;
            s->maxdata = (1 << devpriv->ai_spi->device_spi->n_chan_bits) - 1;
            s->range_table = &range_ads1220_ai;
            s->n_chan = devpriv->ai_spi->device_spi->n_chan;
            s->len_chanlist = devpriv->ai_spi->device_spi->n_chan;
            s->insn_config = daqgert_ai_insn_config;
            if (devpriv->smp) {
                s->subdev_flags = devpriv->ai_spi->device_spi->ai_subdev_flags;
                s->do_cmdtest = daqgert_ai_cmdtest;
                s->do_cmd = daqgert_ai_cmd;
                s->poll = daqgert_ai_poll;
                s->cancel = daqgert_ai_cancel;
            } else {
                s->subdev_flags = devpriv->ai_spi->device_spi->ai_subdev_flags - SDF_CMD_READ;
            }
        }
        if (devpriv->ai_spi->device_type == ads8330 || devpriv->ai_spi->device_type == special) {
            /* we support single-ended (ground) & diff  16-bit samples */
            s->maxdata = (1 << devpriv->ai_spi->device_spi->n_chan_bits) - 1;
            s->range_table = &daqgert_ai_range3_300;
            s->n_chan = devpriv->ai_spi->device_spi->n_chan;
            s->len_chanlist = devpriv->ai_spi->device_spi->n_chan;
            s->insn_config = daqgert_ai_insn_config;
            if (devpriv->smp) {
                s->subdev_flags = devpriv->ai_spi->device_spi->ai_subdev_flags;
                s->do_cmdtest = daqgert_ai_cmdtest;
                s->do_cmd = daqgert_ai_cmd;
                s->poll = daqgert_ai_poll;
                s->cancel = daqgert_ai_cancel;
            } else {
                s->subdev_flags = devpriv->ai_spi->device_spi->ai_subdev_flags - SDF_CMD_READ;
            }
        }

        if (lsamp_size != SDF_LSAMPL) {
            lsamp_size = 0;
            dev_info(dev->class_dev, "16 bit and less device buffers set to 16 bits\n");
        }
        dev->read_subdev = s;

        /* daq-gert ao */
        s = &dev->subdevices[2];
        s->private = devpriv->ao_spi;
        s->type = COMEDI_SUBD_AO;
        /* we support single-ended (ground)  */
        s->n_chan = thisboard->n_aochan;
        s->len_chanlist = thisboard->n_aochan;
        /* analog resolution depends on the DAC chip 8,10,12 bits */
        s->maxdata = (1 << thisboard->n_aochan_bits) - 1;
        s->range_table = &daqgert_ao_range;
        s->insn_write = daqgert_ao_winsn;
        s->insn_read = comedi_readback_insn_read;
        if (devpriv->smp) {
            s->subdev_flags = devpriv->ao_spi->device_spi->ao_subdev_flags;
            s->do_cmdtest = daqgert_ao_cmdtest;
            s->do_cmd = daqgert_ao_cmd;
            s->cancel = daqgert_ao_cancel;
        } else {
            s->subdev_flags = devpriv->ao_spi->device_spi->ao_subdev_flags - SDF_CMD_WRITE;
        }
        ret = comedi_alloc_subdev_readback(s);
        if (ret) {
            dev_err(dev->class_dev,
                "alloc subdevice readback failed!\n");
            return ret;
        }
    }
https://forum.allaboutcircuits.com/threads/raspberry-pi-daq-system.75543/post-865187

https://forum.allaboutcircuits.com/threads/raspberry-pi-daq-system.75543/post-866607
It seems a complex way just to setup a SPI device the Linux way but by using non-global pointers to variables, dynamic variables with queues and lists it's possible to write easily reusable C code that can automatically adjust to changes in the hardware with just a few table entries, avoid data duplication and reduce the number of program locks needed for correct operation on multi-processor machines like the new RPI2 4 core board.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,610
I've introduced several improvements to the crude code I originally played with for these NRF devices. I devised a simple scheme for supporting namespaces in C which improves code readability and totally removed any dependency on STM headers, from the NRF API code.

I've also devised a header management strategy that disciplines the nature of the headers that arise when building such a library.

I did this years ago on a huge Win32 API that managed a multi threaded shared heap (that sat on top of memory mapped files). There are only a handful of distinct "kinds of things" in C and by separating these out the headers become more systematic.

The library "consumer" needs to include just one header xxx_library.h for example, and all structs, typedefs, prototypes, globals, externs etc all get their own distinct class of header file.

The namespace idea means we can write code that looks like this - in C:

Code:
NrfLibrary.Write.SingleByteRegister(device_ptr, NrfRegister.RF_CH, regval, &status);
Since a library is just a collection of single instance methods, we can aggregate them into some structs and define these as single instance also:

NRF24L01_structs.h

Code:
struct nrf_read_calls
{
    void(* SingleByteRegister)(NrfSpiDevice_ptr device_ptr, uint8_t Register, uint8_t * Value, NrfReg_STATUS_ptr NrfStatus);
    void(* MultiBytesRegister)(NrfSpiDevice_ptr device_ptr, uint8_t Register, uint8_t Value[], uint8_t * BytesRead, NrfReg_STATUS_ptr NrfStatus);
};

struct nrf_write_calls
{
    void(* SingleByteRegister)(NrfSpiDevice_ptr device_ptr, uint8_t Register, uint8_t Value, NrfReg_STATUS_ptr NrfStatus);
    void(* MultiBytesRegister)(NrfSpiDevice_ptr device_ptr, uint8_t Register, uint8_t Value[], uint8_t * BytesWritten, NrfReg_STATUS_ptr NrfStatus);
};
struct nrf_library_calls
{
    NrfIoCallbacks_ptr ptr;
    struct nrf_read_calls Read;
    struct nrf_write_calls Write;
};
These are then statically initialized in NRF24L01_globals.h (used only by the library source code)

Code:
NrfLibraryCalls NrfLibrary =
{
    .Read =
    {  
        .SingleByteRegister = _ReadSingleByteRegister,
        .MultiBytesRegister = _ReadMultiBytesRegister,
    },
    .Write =
    {
        .SingleByteRegister = _WriteSingleByteRegister,
        .MultiBytesRegister = _WriteMultiBytesRegister
    }
};
Consumers see these in NRF24L01_externs.h

Code:
extern NrfRegisters NrfRegister;
extern NrfLibraryCalls NrfLibrary;
Also all library method calls are all static (private) and exposed via the function pointers in the "global" library struct.

Consumers of the library just do this:

Code:
#include <NRF24L01_library.h>
and that header just encapsulates the others like this:

Code:
#include <NRF24L01_macros.h>
#include <NRF24L01_typedefs.h>
#include <NRF24L01_structs.h>
#include <NRF24L01_externs.h>
#include <NRF24L01_functions.h>
That's my naming convention, the headers are able to always be ordered in that order too and any conceivable code design can fit this pattern I think.

I found such granular headers a great help in the past when I used C daily on large projects, I forgot much of it but its slowly creeping back!

The project (which although has this structure, is still very minimal functionally) can be found here.
 
Last edited:
Top