LCOV - code coverage report
Current view: top level - src - mcp23016.c (source / functions) Hit Total Coverage
Test: libmcp23016 1.1 Lines: 91 97 93.8 %
Date: 2022-10-15 04:09:25 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1-or-later */
       2             : /*
       3             :  * Copyright (C) 2021 Steven Stallion <sstallion@gmail.com>
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or modify
       6             :  * it under the terms of the GNU Lesser General Public License as published
       7             :  * by the Free Software Foundation; either version 2.1 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
      13             :  * the GNU Lesser General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with this library; if not, see <http://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : #include "mcp23016-private.h"
      20             : 
      21             : #include <assert.h>
      22             : #include <endian.h>
      23             : #include <errno.h>
      24             : #include <stdint.h>
      25             : #include <stdlib.h>
      26             : #include <gpiod.h>
      27             : #include <i2cd.h>
      28             : 
      29           4 : struct mcp23016_device *mcp23016_open(const char *path, unsigned int num)
      30             : {
      31             :         struct mcp23016_device *dev;
      32             : 
      33             :         assert(path != NULL);
      34             : 
      35           4 :         dev = calloc(1, sizeof(*dev));
      36           4 :         if (dev == NULL)
      37           1 :                 return NULL;
      38             : 
      39           3 :         dev->i2c_addr = BASE_ADDR + num;
      40           3 :         if (dev->i2c_addr < BASE_ADDR || dev->i2c_addr > END_ADDR) {
      41           1 :                 errno = EINVAL;
      42           1 :                 goto err;
      43             :         }
      44             : 
      45           2 :         dev->i2c_dev = i2cd_open(path);
      46           2 :         if (dev->i2c_dev == NULL)
      47           1 :                 goto err;
      48             : 
      49           1 :         return dev;
      50           2 : err:
      51           2 :         free(dev);
      52           2 :         return NULL;
      53             : }
      54             : 
      55           1 : void mcp23016_close(struct mcp23016_device *dev)
      56             : {
      57             :         assert(dev != NULL);
      58             : 
      59           1 :         i2cd_close(dev->i2c_dev);
      60             : 
      61           1 :         free(dev);
      62           1 : }
      63             : 
      64           1 : int mcp23016_reset(struct mcp23016_device *dev)
      65             : {
      66             :         int res;
      67             : 
      68             :         assert(dev != NULL);
      69             : 
      70             :         /* The MCP23016 does not provide a hardware reset. The following
      71             :          * sequence resets registers to POR defaults and clears pending
      72             :          * interrupts.
      73             :          */
      74           1 :         res = mcp23016_set_direction(dev, 0xffff);
      75           1 :         if (res < 0)
      76           0 :                 return res;
      77             : 
      78           1 :         res = mcp23016_set_output(dev, 0x0000);
      79           1 :         if (res < 0)
      80           0 :                 return res;
      81             : 
      82           1 :         res = mcp23016_set_polarity(dev, 0x0000);
      83           1 :         if (res < 0)
      84           0 :                 return res;
      85             : 
      86           1 :         res = mcp23016_set_control(dev, 0x0000);
      87           1 :         if (res < 0)
      88           0 :                 return res;
      89             : 
      90           1 :         return mcp23016_clear_interrupt(dev);
      91             : }
      92             : 
      93           7 : int mcp23016_register_read(struct mcp23016_device *dev, uint8_t reg, uint16_t *val)
      94             : {
      95             :         int res;
      96             : 
      97             :         assert(dev != NULL);
      98             :         assert(val != NULL);
      99             : 
     100             :         /* 16-bit registers are accessed by reading an additional byte.
     101             :          * Values are encoded in little-endian byte order.
     102             :          */
     103           7 :         res = i2cd_register_read(dev->i2c_dev, dev->i2c_addr, reg, val, sizeof(*val));
     104           7 :         if (res < 0)
     105           0 :                 return res;
     106             : 
     107           7 :         *val = le16toh(*val);
     108           7 :         return 0;
     109             : }
     110             : 
     111           9 : int mcp23016_register_write(struct mcp23016_device *dev, uint8_t reg, uint16_t val)
     112             : {
     113           9 :         const uint8_t buf[] = {reg, LOW(val), HIGH(val)};
     114             :         int res;
     115             : 
     116             :         assert(dev != NULL);
     117             : 
     118             :         /* 16-bit registers are accessed by writing an additional byte.
     119             :          * Values are encoded in little-endian byte order.
     120             :          */
     121           9 :         res = i2cd_write(dev->i2c_dev, dev->i2c_addr, buf, sizeof(buf));
     122           9 :         if (res < 0)
     123           0 :                 return res;
     124             : 
     125           9 :         return 0;
     126             : }
     127             : 
     128           1 : int mcp23016_get_port(struct mcp23016_device *dev, uint16_t *val)
     129             : {
     130           1 :         return mcp23016_register_read(dev, REG_GP0, val);
     131             : }
     132             : 
     133           1 : int mcp23016_set_port(struct mcp23016_device *dev, uint16_t val)
     134             : {
     135           1 :         return mcp23016_register_write(dev, REG_GP0, val);
     136             : }
     137             : 
     138           1 : int mcp23016_get_output(struct mcp23016_device *dev, uint16_t *val)
     139             : {
     140           1 :         return mcp23016_register_read(dev, REG_OLAT0, val);
     141             : }
     142             : 
     143           2 : int mcp23016_set_output(struct mcp23016_device *dev, uint16_t val)
     144             : {
     145           2 :         return mcp23016_register_write(dev, REG_OLAT0, val);
     146             : }
     147             : 
     148           1 : int mcp23016_get_polarity(struct mcp23016_device *dev, uint16_t *val)
     149             : {
     150           1 :         return mcp23016_register_read(dev, REG_IPOL0, val);
     151             : }
     152             : 
     153           2 : int mcp23016_set_polarity(struct mcp23016_device *dev, uint16_t val)
     154             : {
     155           2 :         return mcp23016_register_write(dev, REG_IPOL0, val);
     156             : }
     157             : 
     158           1 : int mcp23016_get_direction(struct mcp23016_device *dev, uint16_t *val)
     159             : {
     160           1 :         return mcp23016_register_read(dev, REG_IODIR0, val);
     161             : }
     162             : 
     163           2 : int mcp23016_set_direction(struct mcp23016_device *dev, uint16_t val)
     164             : {
     165           2 :         return mcp23016_register_write(dev, REG_IODIR0, val);
     166             : }
     167             : 
     168           2 : int mcp23016_get_interrupt(struct mcp23016_device *dev, uint16_t *val)
     169             : {
     170           2 :         return mcp23016_register_read(dev, REG_INTCAP0, val);
     171             : }
     172             : 
     173           1 : int mcp23016_get_control(struct mcp23016_device *dev, uint16_t *val)
     174             : {
     175           1 :         return mcp23016_register_read(dev, REG_IOCON0, val);
     176             : }
     177             : 
     178           2 : int mcp23016_set_control(struct mcp23016_device *dev, uint16_t val)
     179             : {
     180           2 :         return mcp23016_register_write(dev, REG_IOCON0, val);
     181             : }
     182             : 
     183           5 : struct mcp23016_interrupt *mcp23016_interrupt_open(const char *path, unsigned int offset)
     184             : {
     185             :         struct mcp23016_interrupt *intr;
     186             :         int flags, errsv;
     187             : 
     188             :         assert(path != NULL);
     189             : 
     190           5 :         intr = calloc(1, sizeof(*intr));
     191           5 :         if (intr == NULL)
     192           1 :                 return NULL;
     193             : 
     194           4 :         intr->gpio_chip = gpiod_chip_open(path);
     195           4 :         if (intr->gpio_chip == NULL)
     196           1 :                 goto err;
     197             : 
     198           3 :         intr->gpio_line = gpiod_chip_get_line(intr->gpio_chip, offset);
     199           3 :         if (intr->gpio_line == NULL)
     200           1 :                 goto err;
     201             : 
     202           2 :         flags = GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
     203           2 :         if (gpiod_line_request_input_flags(intr->gpio_line, CONSUMER, flags) < 0)
     204           1 :                 goto err;
     205             : 
     206           1 :         return intr;
     207           3 : err:
     208           3 :         errsv = errno;
     209             : 
     210           3 :         if (intr->gpio_line != NULL)
     211           1 :                 gpiod_line_release(intr->gpio_line);
     212             : 
     213           3 :         if (intr->gpio_chip != NULL)
     214           2 :                 gpiod_chip_close(intr->gpio_chip);
     215             : 
     216           3 :         free(intr);
     217             : 
     218           3 :         errno = errsv;
     219           3 :         return NULL;
     220             : }
     221             : 
     222           1 : void mcp23016_interrupt_close(struct mcp23016_interrupt *intr)
     223             : {
     224             :         assert(intr != NULL);
     225             : 
     226           1 :         gpiod_line_release(intr->gpio_line);
     227           1 :         gpiod_chip_close(intr->gpio_chip);
     228             : 
     229           1 :         free(intr);
     230           1 : }
     231             : 
     232           1 : int mcp23016_has_interrupt(struct mcp23016_interrupt *intr)
     233             : {
     234           1 :         return gpiod_line_get_value(intr->gpio_line);
     235             : }

Generated by: LCOV version 1.14