Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to create a C++ device driver class

I know C very well, not C++ - and want to create a hardware driver in C++. I need some pointers.

In C, I would create a structure, with function pointers and populate the func-pointers accordingly, ether at run time, or by compile time initialization {like the device drivers re initialized in Linux Kernel drivers}. Linux device drivers often have a 'driver private' element that lets the driver keep an implementation specific pointer (holding other stuff)

What I don't know how to do - is create the same thing using C++ classes.

My example would be for a serial port.

The Hardware portion of the driver - in C would be something like this:

struct serail_driver;
struct serial_hw_level;

struct serial_hw_level {
    // points to implementation specific details
    inptr_t  hw_private;

    // basically pure virtual functions
    int (*wrBytes)( struct serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes );
    int (*rdBytes)( struct serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes );

    /* there would be other member funds, but this is is a short example */
};

/* the generic serial port */
struct serial_generic_driver {
     const char *name;

     struct serial_hw_driver *hw_Driver;
};

/* create two serial ports at the hardware level */
struct serial_hw_level arm9_serial_port = {
    .hw_private = (intptr_t)(&hw_driver_
    .wr_bytes   = arm9_uart_wr_bytes,
    .rd_bytes   = arm9_uart_rd_bytes
};

struct serial_hw_level usb_emulated_port = {
    .hw_private = (inptr_t)(&usb_private_stuff),
    .wr_bytes   = usb_serial_wr_bytes,
    .rd_bytes   = usb_serial_rd_bytes
};

/* create generic serial port stuff */
struct serial_port usb_serial = {
   .name = "USBSERIAL",
   .hw_driver = &usb_emulated_port
};

struct serial_port hw_serial = {
    .name = "HW SERIAL",
    .hw_driver = &arm9_serial_port
};

To be clear; I do need the two different levels of abstraction. I know well how to do this in straight C - in the Linux kernel -that is a cake walk.

What I don't know how to do is handle the extra hardware specific stuff using C++ that would be keep in the private data structures. It seems that should be in a derived class or something?

I'm thinking something like this:

/* the base class */
class serial_hw_level {
    intptr_t hw_private;  
    virtual int wr_bytes( uint8_t *pBytes, int bytes );
    virtual int rd_bytes( uint8_t *pBytes, int bytes );
 };

/* The generic usb serial port... */
class serial_port : public serial_hw_level {
    string name;    
};


/* another derived class */    
class usb_serial : public serial_hw_level {
   int usb_endpoint;
   int usb_controller;
   int emualted_handshake_pins;
};

So I sort of have a VEN-Diagram of classes, my question is how do I do this in C++. I can do this in C, it is simple. I completely understand that technique.

What i have is two types of class pointers that need to point to the same base class.

in plain C, I hide things in an implementation specific structure via the 'intptr_t private' - which is a pointer in hiding.

In C++, I think this is kept in the derived class.

The same idea could be extended - and I could create a 'ByteStream" class - that could handle a stream of bytes - either to a file, or to a serial port, or - perhaps to a buffer in memory.

Please note: I'm using a serial port as an example, but these same would apply to an I2C port. I might have a I2C port implemented in hardware, or perhaps implemented via bit-bang IO or SPI port

Thanks.

like image 537
user3696153 Avatar asked Jun 01 '14 06:06

user3696153


People also ask

How do I create a device file?

You can create device file using of mknod command provided by linux. for you case you have to provide c , as you are creating character device file. After creating device file you also have to change permissions of file if you want to manipulate file in future.

Can you write drivers in C?

Device drivers are typically written in C, using the Driver Development Kit (DDK). There are functional and object-oriented ways to program drivers, depending on the language chosen to write in. It is generally not possible to program a driver in Visual Basic or other high-level languages.

What is device driver class?

Device drivers are the implementation of that programming interface for a particular device on a particular bus. Device classes are agnostic with respect to what bus a device resides on.


Video Answer


1 Answers

struct serial_driver;

class serial_hw_level {
public:
    virtual ~serial_hw_level() { }
    virtual int wrBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes) = 0;
    virtual int rdBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes) = 0;
};

class arm9_uart: public serial_hw_level {
public:
    arm9_uart(int sutff);
    virtual ~arm9_uart();
    virtual int wrBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes);
    virtual int rdBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes);

private:
    int arm9_stuff;
};

class usb_emulated: public serial_hw_level {
public:
    usb_emulated(int endpoint, int controller, int handshake_pins);
    virtual ~usb_emulated();
    virtual int wrBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes);
    virtual int rdBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes);

private:
    int usb_endpoint;
    int usb_controller;
    int emulated_handshake_pins;
};

class serial_port {
public:
    serial_port(const string& serial_name, serial_hw_level* driver);
    ~serial_port();
    string get_name() const;

private:
    string name;
    serial_hw_level* hw_driver;
};


// Implementation *.cpp files

arm9_uart::arm9_uart(int stuff)
    : arm9_stuff(stuff) {
}

arm9_uart::~arm9_uart() {
    // free resources
}

int arm9_uart::wrBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes) {
    // do things
    return 0;
}

int arm9_uart::rdBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes) {
    // do things
    return 0;
}


usb_emulated::usb_emulated(int endpoint, int controller, int handshake_pins)
    : usb_endpoint(endpoint),
      usb_controller(controller),
      emulated_handshake_pins(handshake_pins) {
}

usb_emulated::~usb_emulated() {
    // free resources
}

int usb_emulated::wrBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes) {
    // do things
    return 0;
}

int usb_emulated::rdBytes(serial_driver *pDriver, uint8_t *ptr_bytes, int nBytes) {
    // do things
    return 0;
}

serial_port::serial_port(const string& serial_name, serial_hw_level* driver)
    : name(serial_name),
      hw_driver(driver) {
}

serial_port::~serial_port() {
    delete hw_driver;
    hw_driver = NULL;
}

string serial_port::get_name() const {
    return name;
}


serial_port hw_serail = {
    "HW SERIAL",
    new arm9_uart(arm9_stuff)
};

serial_port usb_serial = {
    "USBSERIAL",
    new usb_emulated(usb_endpoint, usb_controller, emulated_handshake_pins)
};

The serial_hw_level is the interface class. Function pointer assignments in C such as wrBytes are done in C++ via virual function pointers when object is created.

The serial_port class, however, is not necessary to be derived from the serial_hw_level. Composition is more suitable for it, since you want a name for it, and also an abstract serial_hw_level object.

The next is the objects in the global namespace. When you want it to be global, in C, you can simply assign function pointers to it. You even don't have a derived class or derived object. All you have are the base class, the base object, and the virtual function pointer assignment(in C++'s view) at the initial time. In C++, when you have derived classes and virtual function pointers, the assignment to base class is achieved by pointers or by references. Since you need global objects, you can new the derived objects, assign them to the base pointer at the initial time, and delete them just before the progress's exit automatically by the destructor of class serial_port.

like image 77
Alaya Bright Avatar answered Oct 17 '22 14:10

Alaya Bright