QNX® Neutrino I2C驅動框架與代碼分析

本文主要描述QNX I2C Drvier的相關內容,並以Texas Instruments DRA71x Jacinto6 Cortex A15處理器爲例講解

I2C 是經常用到的一種總線協議,它只佔用兩個IO口資源,分別是SCL時鐘信號線與SDA數據線,兩根線就能將連接與總線上的設備實現數據通信,由於它的簡便的構造設計,於是成爲一種較爲常用的通信方式。在QNX系統裏,也提供了I2C驅動框架。

目錄結構與組成部分

下面是Texas.Instruments.DRA71x.Jacinto6.Entry.BSP.for.QNX.SDP.6.6包目錄,我們只展開跟I2C相關目錄內容:

.
├── Makefile
├── images
│   ├── Makefile
│   └── mkflashimage.sh
├── install
├── manifest
├── prebuilt
│   ├── armle-v7
│   │   ├── lib
│   │   │   └── dll
│   │   │       └── devu-omap5-xhci.so
│   │   └── usr
│   │       └── lib
│   │           ├── libfs-flash3.a
│   │           ├── libi2c-master.a
│   │           ├── libio-char.a
│   │           ├── libspi-master.a
│   │           ├── libspi-masterS.a
│   │           └── libutil.a
│   └── usr
│       └── include
│           ├── avb.h
│           ├── cam.h
│           ├── fs
│           ├── hw
│           │   └── i2c.h
│           ├── module.h
│           ├── netdrvr
│           └── xpt.h
├── readme.txt
├── source.xml
└── src
    ├── Makefile
    ├── hardware
    │   ├── Makefile
    │   ├── deva
    │   ├── devb
    │   ├── devc
    │   ├── devi
    │   ├── devnp
    │   ├── etfs
    │   ├── flash
    │   ├── i2c
    │   │   ├── Makefile
    │   │   ├── common.mk
    │   │   └── omap35xx
    │   │       ├── Makefile
    │   │       ├── Usemsg
    │   │       ├── Usemsg-omap4
    │   │       ├── arm
    │   │       ├── bus_recover.c
    │   │       ├── bus_speed.c
    │   │       ├── clock_toggle.c
    │   │       ├── common.mk
    │   │       ├── context_restore.c
    │   │       ├── context_restore.h
    │   │       ├── fini.c
    │   │       ├── info.c
    │   │       ├── init.c
    │   │       ├── lib.c
    │   │       ├── module.tmpl
    │   │       ├── offsets.h
    │   │       ├── options.c
    │   │       ├── project.xml
    │   │       ├── proto.h
    │   │       ├── recv.c
    │   │       ├── reg_map_init.c
    │   │       ├── reset.c
    │   │       ├── send.c
    │   │       ├── slave_addr.c
    │   │       ├── version.c
    │   │       └── wait.c
    │   ├── ipl
    │   ├── mtouch
    │   ├── spi
    │   ├── startup
    │   └── support
    └── utils
        ├── Makefile
        └── r

I2C框架由以下部分組成:

hardware/i2c/* 硬件接口

├── hardware
│   ├── i2c
│   │   ├── Makefile
│   │   ├── common.mk
│   │   └── omap35xx
│   │       ├── Makefile
│   │       ├── Usemsg
│   │       ├── Usemsg-omap4
│   │       ├── arm
│   │       ├── bus_recover.c
│   │       ├── bus_speed.c
│   │       ├── clock_toggle.c
│   │       ├── common.mk
│   │       ├── context_restore.c
│   │       ├── context_restore.h
│   │       ├── fini.c
│   │       ├── info.c
│   │       ├── init.c
│   │       ├── lib.c
│   │       ├── module.tmpl
│   │       ├── offsets.h
│   │       ├── options.c
│   │       ├── project.xml
│   │       ├── proto.h
│   │       ├── recv.c
│   │       ├── reg_map_init.c
│   │       ├── reset.c
│   │       ├── send.c
│   │       ├── slave_addr.c
│   │       ├── version.c
│   │       └── wait.c

lib/i2c 資源管理器層

├── prebuilt
│   ├── armle-v7
│   │   ├── lib
│   │   │   └── dll
│   │   │       └── devu-omap5-xhci.so
│   │   └── usr
│   │       └── lib
│   │           ├── libfs-flash3.a
│   │           ├── libi2c-master.a
│   │           ├── libio-char.a
│   │           ├── libspi-master.a
│   │           ├── libspi-masterS.a
│   │           └── libutil.a

<hw/i2c.h> 定義硬件和應用程序接口的公共頭文件

├── prebuilt
│   ├── armle-v7
│   │   ├── lib
│   │   │   └── dll
│   │   └── usr
│   │       └── lib
│   └── usr
│       └── include
│           ├── hw
│           │   └── i2c.h

2C總線最常見的應用是對從設備寄存器的低帶寬、低速率訪問,例如:編寫音頻編解碼器、編程一個RTC程序、讀取溫度傳感器數據等等。 通常,總線上只交換幾個字節。可以將I2C主機實現爲單線程資源管理器或專用應用程序。資源管理器接口的主要優點是:

1、它爲應用程序開發人員提供了一個清晰、易於理解的思路。

2、它作爲一箇中介,在多個應用程序對一個或多個從設備之間進行訪問,強制不同I2C接口之間的一致性。

3、對於專用的i2c總線應用程序,硬件訪問庫更有效;硬件接口定義了這個庫的接口,有助於維護和代碼可移植性。

QNX I2C驅動提供了基本的硬件操作接口,其接口名稱爲i2c_master_funcs_t,這些接口是驅動裏要求實現,包括讀寫、設置地址、總線速度、版本信息等等。每個接口對應於上面的一個文件.c,這樣便於模塊化。

硬件管理接口

typedef struct {
    size_t size;    /* size of this structure */
    int (*version_info)(i2c_libversion_t *version);
    void *(*init)(int argc, char *argv[]);
    void (*fini)(void *hdl);
    i2c_status_t (*send)(void *hdl, void *buf, unsigned int len, 
                         unsigned int stop);
    i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len, 
                         unsigned int stop);
    int (*abort)(void *hdl, int rcvid);
    int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
    int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
    int (*driver_info)(void *hdl, i2c_driver_info_t *info);
    int (*ctl)(void *hdl, int cmd, void *msg, int msglen, 
               int *nbytes, int *info);
    int (*bus_reset)(void *hdl);
} i2c_master_funcs_t;

在hardware/i2c/目錄中,能找到對應函數的實現。

├── i2c
│   ├── Makefile
│   ├── common.mk
│   └── omap35xx
│       ├── Makefile
│       ├── Usemsg
│       ├── Usemsg-omap4
│       ├── arm
│       ├── bus_recover.c
│       ├── bus_speed.c       # int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
│       ├── clock_toggle.c
│       ├── common.mk
│       ├── context_restore.c
│       ├── context_restore.h
│       ├── fini.c             # void (*fini)(void *hdl);
│       ├── info.c             # int (*driver_info)(void *hdl, i2c_driver_info_t *info);
│       ├── init.c             # void *(*init)(int argc, char *argv[]);
│       ├── lib.c
│       ├── module.tmpl
│       ├── offsets.h
│       ├── options.c
│       ├── project.xml
│       ├── proto.h
│       ├── recv.c             # i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len,  unsigned int stop);
│       ├── reg_map_init.c
│       ├── reset.c            # int (*bus_reset)(void *hdl);
│       ├── send.c             # i2c_status_t (*send)(void *hdl, void *buf, unsigned int len, unsigned int stop);
│       ├── slave_addr.c       # int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
│       ├── version.c          # int (*version_info)(i2c_libversion_t *version);
│       └── wait.c

硬件訪問接口

這是整個硬件接口函數,提供了10個接口函數,分別對10個接口函數進行介紹:

version_info函數

version_info函數來獲取關於庫版本的信息。這個函數的原型是:

int (*version_info)(i2c_libversion_t *version);

version參數是指向這個函數必須填充的i2c_libversion_t結構的指針。該結構定義如下:

typedef struct {
    unsigned char   major;
    unsigned char   minor;
    unsigned char   revision;
} i2c_libversion_t;

具體驅動實現:

int omap_version_info(i2c_libversion_t *version)
{
    version->major = I2CLIB_VERSION_MAJOR;
    version->minor = I2CLIB_VERSION_MINOR;
    version->revision = I2CLIB_REVISION;
    return 0;
}

init函數

init函數初始化主接口。該函數的原型是:

void *(*init)(int argc, char *argv[]);

參數是在命令行上傳遞的。函數返回傳遞給所有其他函數的句柄,如果發生錯誤則返回NULL。這個函數主要根據命令行解析來進行I2C初始化。

具體驅動實現:

void *omap_init(int argc, char *argv[])
{
    omap_dev_t      *dev;
        uint16_t                s;

    if (-1 == ThreadCtl(_NTO_TCTL_IO, 0)) {
        perror("ThreadCtl");
        return NULL;
    }

    dev = malloc(sizeof(omap_dev_t));
    if (!dev)
        return NULL;
        dev->speed = 100000;
    //根據命令 配置I2C相關參數包括基地址 中斷號
    if (-1 == omap_options(dev, argc, argv)) {
                fprintf(stderr, "omap_options: parse I2C option failed\n");
        goto fail_free_dev;
    }
    //I2C 物理地址映射
    dev->regbase = mmap_device_io(dev->reglen, dev->physbase);
    if (dev->regbase == (uintptr_t)MAP_FAILED) {
        perror("mmap_device_io");
        goto fail_free_dev;
    }

    /* Initialize interrupt handler */
    if ((dev->chid = ChannelCreate(_NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK)) == -1) {
        perror("ChannelCreate");
        goto fail_unmap_io;
    }

    if ((dev->coid = ConnectAttach(0, 0, dev->chid, _NTO_SIDE_CHANNEL, 0)) == -1) {
        perror("ConnectAttach");
        goto fail_chnl_dstr;
    }

        dev->intrevent.sigev_notify   = SIGEV_PULSE;
        dev->intrevent.sigev_coid     = dev->coid;
        dev->intrevent.sigev_code     = OMAP_I2C_EVENT;
        dev->intrevent.sigev_priority = dev->intr_priority;

        /*
         * Attach interrupt
         */
        dev->iid = InterruptAttach(dev->intr, i2c_intr, dev, 0, _NTO_INTR_FLAGS_TRK_MSK);
    if (dev->iid == -1) {
        perror("InterruptAttachEvent");
        goto fail_con_dtch;
    }

        if (omap_reg_map_init(dev) == -1) {
                goto fail_intr_dtch;
        }

        if (context_restore_init(dev) == -1) {
                goto fail_ctxt_rest;
        }

        if (omap_clock_toggle_init(dev) == -1) {
                goto fail_ctxt_rest;
        }
        // I2C 系統時鐘初始化
        /* Set up the fifo size - Get total size */
        omap_clock_enable(dev);
        s = (in16(dev->regbase + OMAP_I2C_BUFSTAT) >> 14) & 0x3;
        omap_clock_disable(dev);
        dev->fifo_size = 0x8 << s;
        /* Set up notification threshold as half the total available size. */
        dev->fifo_size >>=1;

        if (omap_i2c_reset(dev) == -1) {
                fprintf(stderr, "omap_i2c_reset: reset I2C interface failed\n");
                goto fail_ctxt_rest;
        }

    return dev;
//失敗退出處理
fail_ctxt_rest:
    context_restore_fini(dev);
fail_intr_dtch:
    InterruptDetach(dev->iid);
fail_con_dtch:
        ConnectDetach(dev->coid);
fail_chnl_dstr:
        ChannelDestroy(dev->chid);
fail_unmap_io:
    munmap_device_io(dev->regbase, dev->reglen);
fail_free_dev:
    free(dev);
    return NULL;
}

fini函數

fini函數清理驅動程序並釋放與給定句柄關聯的所有內存。該函數的原型是:

void (*fini)(void *hdl);

具體源碼實現:

void omap_fini(void *hdl)
{
    omap_dev_t  *dev = hdl;

    omap_clock_enable(dev);
    out16(dev->regbase + OMAP_I2C_CON, 0);
    out16(dev->regbase + OMAP_I2C_IE, 0);
    omap_clock_disable(dev);
    InterruptDetach(dev->iid); // 釋放中斷映射
    ConnectDetach(dev->coid);  // 斷開消息傳遞(Message-passing)Channel連接
    ChannelDestroy(dev->chid); // 釋放消息傳遞(Message-passing)Channel
    // 時鐘控制物理地址映射釋放
    if (dev->clkctrl_base) {
        munmap_device_io (dev->clkctrl_base, 4);
    }

    if (dev->clkstctrl_base) {
        munmap_device_io (dev->clkstctrl_base, 4);
    }

    context_restore_fini(dev);
    // 整個I2C控制器物理地址映射釋放
    munmap_device_io (dev->regbase, dev->reglen);
    free (hdl);
}

send函數

發送函數啓動主發送。通信完成時將發送一個可選事件(可以釋放數據緩衝區)。如果此函數失敗,則未啓動任何通信。

該函數的原型是:

i2c_status_t (*send)(void *hdl, void *buf, unsigned int len, unsigned int stop);
/*
* Master send.
* Parameters:
* (in)     hdl         Handle returned from init()  init函數返回的句柄;
* (in)     buf         Buffer of data to send       指向要發送的數據緩衝區的指針;
* (in)     len         Length in bytes of buf       要發送的數據的長度(以字節爲單位);
* (in)     stop        If !0, set stop condition when send completes   要發送的數據的長度(以字節爲單位);
* Returns:
* bitmask of status bits
返回狀態爲:
I2C_STATUS_DONE:傳輸完成,並且沒有錯誤。
I2C_STATUS_ERROR:傳輸錯誤
I2C_STATUS_NACK:沒有ACK
I2C_STATUS_ARBL:失去了仲裁。
I2C_STATUS_BUSY:傳輸超時
I2C_STATUS_ABORT:傳輸終止
*/

具體源碼實現:

i2c_status_t omap_send(void *hdl, void *buf, unsigned int len, unsigned int stop)
{
    omap_dev_t      *dev = hdl;
    i2c_status_t    ret = I2C_STATUS_ERROR;
    int num_bytes;

    if (len <= 0)
        return I2C_STATUS_DONE;

    if (-1 == omap_wait_bus_not_busy(dev, stop))
        return I2C_STATUS_BUSY;

    omap_clock_enable(dev);

        dev->xlen = len;
        dev->buf = buf;
        dev->status = 0;
        dev->intexpected = 1;

    /* set slave address */
    if (dev->slave_addr_fmt == I2C_ADDRFMT_7BIT)
        out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) & (~OMAP_I2C_CON_XSA));
    else
        out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) | OMAP_I2C_CON_XSA);

    out16(dev->regbase + OMAP_I2C_SA, dev->slave_addr);

    /* set data count */
    out16(dev->regbase + OMAP_I2C_CNT, len);

    /* Clear the FIFO Buffers */
	out16(dev->regbase + OMAP_I2C_BUF, in16(dev->regbase + OMAP_I2C_BUF)| OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR);

    /* pre-fill the fifo with outgoing data */
    if (dev->xlen > dev->fifo_size)
      num_bytes = dev->fifo_size;
    else
      num_bytes = dev->xlen;
    while (num_bytes)
    {
      out8(dev->regbase + OMAP_I2C_DATA, *dev->buf++);
      dev->xlen--;
      num_bytes--;
    }

    /* set start condition */
    out16(dev->regbase + OMAP_I2C_CON,
            OMAP_I2C_CON_EN  |
            OMAP_I2C_CON_MST |
            OMAP_I2C_CON_TRX |
            OMAP_I2C_CON_STT |
            (stop? OMAP_I2C_CON_STP : 0)|
            (in16(dev->regbase + OMAP_I2C_CON)&OMAP_I2C_CON_XA));

        ret=  omap_wait_status(dev);
        omap_clock_disable(dev);
    return ret;
}

recv函數

recv函數啓動主接收。傳輸完成時將發送一個可選事件(可以使用數據緩衝區)。如果此函數失敗,則未啓動任何傳輸。

該函數的原型是:

i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len, unsigned int stop);
/*
* Master receive.
* Parameters:
* (in)     hdl         Handle returned from init()   init函數返回的句柄;
* (in)     buf         Buffer for received data      指向要接收的數據緩衝區的指針;
* (in)     len         Length in bytes of buf        要接收的數據的長度(以字節爲單位);
* (in)     stop        If !0, set stop condition when recv completes    如果這是非零的,函數將在發送完成時設置停止條件;
* Returns:
* bitmask of status bits
返回狀態爲:
I2C_STATUS_DONE:傳輸完成,並且沒有錯誤。
I2C_STATUS_ERROR:傳輸錯誤
I2C_STATUS_NACK:沒有ACK
I2C_STATUS_ARBL:失去了仲裁。
I2C_STATUS_BUSY:傳輸超時
I2C_STATUS_ABORT:傳輸終止
*/

具體源碼實現:

i2c_status_t omap_recv(void *hdl, void *buf, unsigned int len, unsigned int stop)
{
    omap_dev_t      *dev = hdl;
    i2c_status_t    ret;

    if (len <= 0)
        return I2C_STATUS_DONE;

    if (-1 == omap_wait_bus_not_busy(dev, stop))
        return I2C_STATUS_BUSY;

        dev->xlen = len;
        dev->buf = buf;
        dev->status = 0;
        dev->intexpected = 1;

        omap_clock_enable(dev);

    /* set slave address */
    if (dev->slave_addr_fmt == I2C_ADDRFMT_7BIT)
        out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) & (~OMAP_I2C_CON_XSA));
    else
        out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) | OMAP_I2C_CON_XSA);

    out16(dev->regbase + OMAP_I2C_SA, dev->slave_addr);

    /* set data count */
    out16(dev->regbase + OMAP_I2C_CNT, len);

	/* Clear the FIFO Buffers */
	out16(dev->regbase + OMAP_I2C_BUF, in16(dev->regbase + OMAP_I2C_BUF)| OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR);

    /* set start condition */
    out16(dev->regbase + OMAP_I2C_CON,
            OMAP_I2C_CON_EN  |
            OMAP_I2C_CON_MST |
            OMAP_I2C_CON_STT |
            (stop? OMAP_I2C_CON_STP : 0) |
            (in16(dev->regbase + OMAP_I2C_CON)&OMAP_I2C_CON_XA));

    ret=  omap_wait_status(dev);

	omap_clock_disable(dev);

    return ret;
}

abort函數

中止功能迫使主程序釋放總線。當停止條件被髮送時,它返回。

該函數的原型是:

int (*abort)(void *hdl, int rcvid);
/*
* Force the master to free the bus.
* Returns when the stop condition has been sent.
* Returns:
* 0    success
* -1   failure
*/

此處I2C驅動不需要進行abort操作。所以abort函數在resmgr層註冊的時候爲NULL。

set_slave_addr函數

set_slave_addr函數指定目標從地址。該函數的原型是:

int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
/*
hdl:  init函數返回的句柄。
addr: 目標從地址。
fmt:地址的格式; I2C_ADDRFMT_7BIT 和I2C_ADDRFMT_10BIT
*/

具體源碼實現:

int omap_set_slave_addr(void *hdl, unsigned int addr, i2c_addrfmt_t fmt)
{
    omap_dev_t      *dev = hdl;

    if(fmt != I2C_ADDRFMT_7BIT && fmt != I2C_ADDRFMT_10BIT)
        return -1;

    dev->slave_addr = addr;
    dev->slave_addr_fmt = fmt;
    return 0;
}

set_bus_speed函數

set_bus_speed函數指定總線速度。如果請求的總線速度無效,該函數將返回一個失敗,並保持總線速度不變。

該函數的原型是:

int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
 /*
* Specify the bus speed.
* If an invalid bus speed is requested, this function should return
* failure and leave the bus speed unchanged.
* Parameters:
* (in)     hdl         Handle returned from init()   init函數返回的句柄;
* (in)     speed       Bus speed. Units are implementation-defined.    總線的速度。
* (out)    ospeed      Actual bus speed (if NULL, this is ignored)     NULL或指向函數應該存儲實際總線速度的位置的指針。
* Returns:
* 0    success
* -1   failure
*/

具體源碼實現:

int
omap_set_bus_speed(void *hdl, unsigned int speed, unsigned int *ospeed)
{
    omap_dev_t      *dev = hdl;
    unsigned long   iclk;
    unsigned        scll_plus_sclh;
    int             scll_delta;
    int             high_adjust;
    int             low_adjust;
    int             scll_to_write;
    int             sclh_to_write;

    /* This driver support bus speed range from 8KHz to 400KHz
     * limit the low bus speed to 8KHz to protect SCLL/SCLH from overflow(large than 0xff)
     * if speed=8KHz, iclk=4MHz, then SCLL=0xf3, SCLH=0xf5
     */
    if (speed > 400000 || speed < 8000) {
        fprintf(stderr, "i2c-omap35xx:  Invalid bus speed(%d)\n", speed);
        errno = EINVAL;
        return -1;
    }

    omap_clock_enable(dev);

    /* Set the I2C prescaler register to obtain the maximum I2C bit rates
     * and the maximum period of the filtered spikes in F/S mode:
     * Stander Mode: I2Ci_INTERNAL_CLK = 4 MHz
     * Fast Mode:    I2Ci_INTERNAL_CLK = 9.6 MHz
     */
    if (speed <= 100000) {
#ifdef VARIANT_j5
        out16(dev->regbase + OMAP_I2C_PSC, 11);     // The sysclk is 48 MHz in J5 and 96 MHz in OMAP
#else
        out16(dev->regbase + OMAP_I2C_PSC, 23);     // I2Ci_INTERNAL_CLK = 4 MHz
#endif
        iclk = OMAP_I2C_ICLK;
        high_adjust = dev->high_adjust_slow;
        low_adjust = dev->low_adjust_slow;
        // in standard mode, scll is smaller than (scll_plus_sclh>>1) by 1
        scll_delta = -1;
    } else {
#ifdef VARIANT_j5
        out16(dev->regbase + OMAP_I2C_PSC, 4);      // The sysclk is 48 MHz in J5 and 96 MHz in OMAP
#else
        out16(dev->regbase + OMAP_I2C_PSC, 9);      // I2Ci_INTERNAL_CLK = 9.6 MHz
#endif
        iclk = OMAP_I2C_ICLK_9600K;
        high_adjust = dev->high_adjust_fast;
        low_adjust = dev->low_adjust_fast;
#ifdef VARIANT_omap4
        // in fast mode, scll is larger than (scll_plus_sclh>>1) by 1
        scll_delta = 1;
#else
        // At this point it is unclear whether omap3 also has this relationship
        // between SCLL and SCLH in fast mode, so we leave it alone for now to
        // avoid breakage.
        scll_delta = -1;
#endif
    }

    /* Set clock based on "speed" bps */
    scll_plus_sclh = (iclk/speed - (SCLL_BIAS + SCLH_BIAS));

    scll_to_write = (scll_plus_sclh>>1) + scll_delta;
    sclh_to_write = scll_plus_sclh - scll_to_write;
    scll_to_write += low_adjust;
    sclh_to_write += high_adjust;

    if (scll_to_write < 0) scll_to_write = 0;
    if (sclh_to_write <= 2) sclh_to_write = 3;

    out16(dev->regbase + OMAP_I2C_SCLL, scll_to_write);
    out16(dev->regbase + OMAP_I2C_SCLH, sclh_to_write);
    dev->speed = iclk / (scll_to_write + SCLL_BIAS + sclh_to_write + SCLH_BIAS);

    if (ospeed)
        *ospeed = dev->speed;

    omap_clock_disable(dev);
    return 0;
}

driver_info函數

driver_info函數返回有關驅動程序的信息。該函數的原型是:

 int (*driver_info)(void *hdl, i2c_driver_info_t *info);
/*
hdl            init函數返回的句柄
info
一個指向i2c_driver_info_t結構的指針,函數應該在該結構中存儲信息:
typedef struct {
    uint32_t    speed_mode;
    uint32_t    addr_mode;
    uint32_t    reserved[2];
} i2c_driver_info_t;
*/

具體源碼實現:

int omap_driver_info(void *hdl, i2c_driver_info_t *info)
{

    info->speed_mode = I2C_SPEED_STANDARD | I2C_SPEED_FAST;
    info->addr_mode = I2C_ADDRFMT_7BIT | I2C_ADDRFMT_10BIT;

    return 0;
}

ctl函數

ctl函數處理一個驅動程序特定的devctl()命令。該函數的原型是:

int (*ctl)(void *hdl, int cmd, void *msg, int msglen, int *nbytes, int *info);
/*
* Handle a driver-specific devctl().
* Parameters:
* (in)     hdl         Handle returned from init()   init函數返回的句柄;
* (in)     cmd         Device command                設備命令;
* (i/o)    msg         Message buffer                一個指向消息緩衝區的指針。該函數可以更改緩衝區的內容;
* (in)     msglen      Length of message buffer in bytes    消息緩衝區的長度,以字節爲單位;
* (out)    nbytes      Bytes to return (<= msglen)          消息緩衝區的長度,以字節爲單位;
* (out)    info        Extra status information returned by devctl    指向函數可以存儲devctl()返回狀態信息的位置指針;
* Returns:
* EOK      success
* errno    failure
*/

此處I2C驅動不需要進行devctrl操作。所以ctl函數在resmgr層註冊的時候爲NULL。

共享庫的接口

當I2C主設備專用於單個應用程序使用時,可以將硬件接口代碼編譯爲庫並將其鏈接到應用程序。

爲了提高代碼的可移植性,應用程序應該調用i2c_master_getfuncs()來訪問特定於硬件的函數。通過功能表訪問硬件庫,應用程序更容易加載和管理多個硬件庫。

資源管理器接口以類似的方式使用硬件庫。

資源管理器設計

資源管理器層實現爲靜態鏈接到硬件庫的庫,提供了一個單線程管理器,libi2c-master。

在啓動時,resmgr層執行以下操作:

i2c_master_getfuncs(&masterf) masterf.init() masterf.set_bus_speed() 然後,資源管理器讓自己在後臺運行,下面是資源管理器處理這些devctl()命令:

DCMD_I2C_DRIVER_INFO calls masterf.driver_info(). DCMD_I2C_SET_BUS_SPEED and DCMD_I2C_SET_SLAVE_ADDR only update the state of the current connection. DCMD_I2C_SEND, DCMD_I2C_RECV, DCMD_I2C_SENDRECV, DCMD_I2C_MASTER_SEND, and DCMD_I2C_MASTER_RECV

if (bus_speed has changed)
    masterf.set_bus_speed()
masterf.set_slave_address()
masterf.send() or masterf.recv()

資源管理器線程一直佔用,直到傳輸完成並回復客戶端,可以通過向資源管理器發送SIGTERM來終止它。

實質和下面流程一樣。在初始化裏設置一箇中斷事件,這個事件將發送消息告訴主循環接收和發送數據。

#include <hw/i2c.h>    
i2c_master_funcs_t  masterf;    
i2c_libversion_t    version;    
i2c_status_t        status;    void *hdl;     
i2c_master_getfuncs(&masterf, sizeof(masterf));//初始化硬件接口     masterf.version_info(&version);    
if ((version.major != I2CLIB_VERSION_MAJOR) || (version.minor > I2CLIB_VERSION_MINOR))    
{
    /* error */        
    ...    
}   
 hdl = masterf.init(...); //調用初始化函數    
 masterf.set_bus_speed(hdl, ...); //設置總線速度 
while(1)
{ 
    InterruptWait (NULL, NULL);
    masterf.set_slave_addr(hdl, ...); //設置從地址     
    status = masterf.send(hdl, ...);//發送數據    
    if (status != I2C_STATUS_DONE) {        /* error */        
    if (!(status & I2C_STATUS_DONE))         
       masterf.abort(hdl);    }    
     status = masterf.recv(hdl, ...);//接收數據    
    if (status != I2C_STATUS_DONE) 
    {        /* error */        ...    }   
    }
      masterf.fini(hdl);//完成I2C傳輸後,清理驅動程序並釋放與給定句柄關聯的所有內存
      InterruptUnmask(INTNUM, id);
}
masterf.fini(hdl);//完成I2C傳輸後,清理驅動程序並釋放與給定句柄關聯的所有內存

參考文獻:

QNX----I2C驅動框架

QNX---IMX6UL I2C 驅動分析

Technical Notes I2C (Inter-Integrated Circuit) Framework(qnx.com)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章