mirror of
https://github.com/apache/nuttx.git
synced 2025-12-06 17:23:49 +08:00
drivers/1wire/1wire_ds2xxx.h: add the driver for DS2XXX eeproms.
Supports these Maxim/Analog Devices eeproms with a scratchpad: DS2430,2431,2432,2433,28E04,28E07,28EC20. For each type of an eeprom, you create a new driver. Other than that, the driver is file oriented and supports seeks, for example. Signed-off-by: Stepan Pressl <pressl.stepan@gmail.com>
This commit is contained in:
committed by
Alin Jerpelea
parent
d1e1643b84
commit
84b0492fbd
829
drivers/1wire/1wire_ds2xxx.c
Normal file
829
drivers/1wire/1wire_ds2xxx.c
Normal file
@@ -0,0 +1,829 @@
|
||||
/****************************************************************************
|
||||
* drivers/1wire/1wire_ds2xxx.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Author: Stepan Pressl <pressl.stepan@gmail.com>
|
||||
* <pressste@fel.cvut.cz>
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* This is a driver for 1Wire Maxim Integrated DS2xxx EEPROMs.
|
||||
* Currently, the driver was tested against the DS2431 EEPROM but other
|
||||
* EEPROMs listed in 1wire_ds2xxx.h
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <debug.h>
|
||||
#include <nuttx/fs/fs.h>
|
||||
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/mutex.h>
|
||||
#include <nuttx/1wire/1wire.h>
|
||||
#include <nuttx/1wire/1wire_crc.h>
|
||||
#include <nuttx/1wire/1wire_ds2xxx.h>
|
||||
#include <nuttx/1wire/1wire_master.h>
|
||||
#include <nuttx/arch.h>
|
||||
|
||||
#include "1wire_internal.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_ENDIAN_BIG
|
||||
# define onewire_leuint64(x) (x)
|
||||
# define onewire_leuint32(x) (x)
|
||||
#endif
|
||||
|
||||
#define WS_HEADER_OFFSET ((uint8_t)3)
|
||||
#define RS_HEADER_SIZE ((uint8_t)3)
|
||||
#define CS_HEADER_SIZE ((uint8_t)4)
|
||||
#define HELPBUF_MARGIN ((uint8_t)5)
|
||||
#define MATCHROM_BUF_SIZE ((uint8_t)9)
|
||||
#define DS2XXX_TPROG_US ((useconds_t)12000)
|
||||
|
||||
/****************************************************************************
|
||||
* Priavate Types
|
||||
****************************************************************************/
|
||||
|
||||
struct ds2xxx_dev_s
|
||||
{
|
||||
FAR struct onewire_master_s *master;
|
||||
|
||||
uint64_t romcode; /* Romcode to be addressed on the bus */
|
||||
int devtype; /* See enum ds2xxx_eeproms_e */
|
||||
uint8_t *helpbuf; /* Helpful memory to avoid alloc on stack */
|
||||
uint8_t refs; /* Opened references to ds2xxx */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
static int ds2xxx_open(FAR struct file *filep);
|
||||
static int ds2xxx_close(FAR struct file *filep);
|
||||
static int ds2xxx_ioctl(FAR struct file *filep, int cmd,
|
||||
unsigned long arg);
|
||||
static ssize_t ds2xxx_read(FAR struct file *filep, FAR char *buffer,
|
||||
size_t buflen);
|
||||
static ssize_t ds2xxx_read_internal(FAR struct ds2xxx_dev_s *priv,
|
||||
off_t eeprom_addr,
|
||||
FAR uint8_t *buffer, size_t buflen);
|
||||
static ssize_t ds2xxx_write(FAR struct file *filep, FAR const char *buffer,
|
||||
size_t buflen);
|
||||
static ssize_t ds2xxx_write_internal(FAR struct ds2xxx_dev_s *priv,
|
||||
off_t eeprom_addr,
|
||||
FAR uint8_t *buffer, size_t buflen);
|
||||
static off_t ds2xxx_seek(FAR struct file *filep, off_t offset, int whence);
|
||||
static int ds2xxx_ioctl(FAR struct file *filep, int cmd,
|
||||
unsigned long arg);
|
||||
|
||||
/* Mapping from ds2xxx_types_e to eeprom sizes, pages and family codes.
|
||||
* The driver is fully generic for all supported DS2XXX memories with
|
||||
* a write scratchpad.
|
||||
*/
|
||||
|
||||
static size_t g_eeprom_sizes[EEPROM_DS_COUNT] =
|
||||
{
|
||||
32,
|
||||
128,
|
||||
128,
|
||||
512,
|
||||
512,
|
||||
128,
|
||||
2560
|
||||
};
|
||||
|
||||
static size_t g_eeprom_scratchpad_sizes[EEPROM_DS_COUNT] =
|
||||
{
|
||||
8,
|
||||
8,
|
||||
8,
|
||||
32,
|
||||
32,
|
||||
8,
|
||||
32
|
||||
};
|
||||
|
||||
static int g_eeprom_familycodes[EEPROM_DS_COUNT] =
|
||||
{
|
||||
0x14,
|
||||
0x2d,
|
||||
0xb3,
|
||||
0x23,
|
||||
0x1c,
|
||||
0x2d,
|
||||
0x43
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const struct file_operations g_ds2xxx_fops =
|
||||
{
|
||||
.open = ds2xxx_open,
|
||||
.close = ds2xxx_close,
|
||||
.read = ds2xxx_read,
|
||||
.write = ds2xxx_write,
|
||||
.seek = ds2xxx_seek,
|
||||
.ioctl = ds2xxx_ioctl,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ENDIAN_BIG
|
||||
static inline uint64_t onewire_leuint64(uint64_t x)
|
||||
{
|
||||
return (((x & 0xff00000000000000ull) >> 56) |
|
||||
((x & 0x00ff000000000000ull) >> 40) |
|
||||
((x & 0x0000ff0000000000ull) >> 24) |
|
||||
((x & 0x000000ff00000000ull) >> 8) |
|
||||
((x & 0x00000000ff000000ull) << 8) |
|
||||
((x & 0x0000000000ff0000ull) << 24) |
|
||||
((x & 0x000000000000ff00ull) << 40) |
|
||||
((x & 0x00000000000000ffull) << 56));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ENDIAN_BIG
|
||||
static inline uint32_t onewire_leuint32(uint32_t x)
|
||||
{
|
||||
return (((x & 0xff000000) >> 24) |
|
||||
((x & 0x00ff0000) >> 8) |
|
||||
((x & 0x0000ff00) << 8) |
|
||||
((x & 0x000000ff) << 24));
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_startcomm
|
||||
*
|
||||
* Description:
|
||||
* Performs the Match Rom command on the bus.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int ds2xxx_startcomm(struct ds2xxx_dev_s *priv, uint8_t *wrbuf)
|
||||
{
|
||||
int ret;
|
||||
uint64_t tmp;
|
||||
|
||||
ret = ONEWIRE_RESET(priv->master->dev);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
wrbuf[0] = DS2XXX_MATCH_ROM;
|
||||
tmp = onewire_leuint64(priv->master->selected_rom);
|
||||
memcpy(&wrbuf[1], &tmp, MATCHROM_BUF_SIZE - 1);
|
||||
|
||||
return ONEWIRE_WRITE(priv->master->dev, wrbuf, MATCHROM_BUF_SIZE);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_read_internal
|
||||
*
|
||||
* Description:
|
||||
* Begins communication and reads data from the EEPROM. The read procedure
|
||||
* is much simpler as data can be read from any address.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t ds2xxx_read_internal(FAR struct ds2xxx_dev_s *priv,
|
||||
off_t eeprom_addr,
|
||||
FAR uint8_t *buffer, size_t buflen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The process of reading is much simpler.
|
||||
* The master must issue the Read Memory command followed with 2 bytes
|
||||
* of address to be read from. The address is sent in little endian order.
|
||||
*/
|
||||
|
||||
/* Initialize the communication */
|
||||
|
||||
ret = ds2xxx_startcomm(priv, buffer);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->helpbuf[0] = DS2XXX_READ_MEMORY;
|
||||
priv->helpbuf[1] = eeprom_addr & 0xff;
|
||||
priv->helpbuf[2] = eeprom_addr >> 8;
|
||||
ret = ONEWIRE_WRITE(priv->master->dev, priv->helpbuf, 3);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Now read real data. */
|
||||
|
||||
ret = ONEWIRE_READ(priv->master->dev, (uint8_t *)buffer, buflen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_write_internal
|
||||
*
|
||||
* Description:
|
||||
* Begins communication and reads data from the EEPROM. The write procedure
|
||||
* is not that straightforward. It is assumed the writes by this function
|
||||
* are aligned. It is assumed buflen is at most the size of the scratchpad.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t ds2xxx_write_internal(FAR struct ds2xxx_dev_s *priv,
|
||||
off_t eeprom_addr,
|
||||
FAR uint8_t *buffer, size_t buflen)
|
||||
{
|
||||
ssize_t ret;
|
||||
uint8_t buf[32 + HELPBUF_MARGIN];
|
||||
uint16_t crc;
|
||||
uint16_t reccrc;
|
||||
|
||||
i2cinfo("writing %zu bytes to %ld", buflen, eeprom_addr);
|
||||
|
||||
ret = ds2xxx_startcomm(priv, buf);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Now issue the write scratchpad command. Also calculate
|
||||
* CRC16 which must be compared with the received one.
|
||||
*/
|
||||
|
||||
buf[0] = DS2XXX_WRITE_SCRATCHPAD;
|
||||
buf[1] = eeprom_addr & 0xff;
|
||||
buf[2] = eeprom_addr >> 8;
|
||||
crc = onewire_crc16(buf, 3, 0);
|
||||
|
||||
ret = ONEWIRE_WRITE(priv->master->dev, buf, 3);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
crc = onewire_crc16(buffer, buflen, crc);
|
||||
ret = ONEWIRE_WRITE(priv->master->dev, buffer, buflen);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We now need to receive the CRC. The order is little endian. */
|
||||
|
||||
ret = ONEWIRE_READ(priv->master->dev, buf, 2);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
reccrc = buf[0] | (buf[1] << 8);
|
||||
if (reccrc != (uint16_t) ~crc)
|
||||
{
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* We have successfully performed the Write Scratchpad command.
|
||||
* Now issue read scratchpad.
|
||||
*/
|
||||
|
||||
ret = ds2xxx_startcomm(priv, buf);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf[0] = DS2XXX_READ_SCRATCHPAD;
|
||||
ret = ONEWIRE_WRITE(priv->master->dev, buf, 1);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
crc = onewire_crc16(buf, 1, 0);
|
||||
|
||||
/* TA-E/S + buflen + ~CRC16 => buflen + 5 */
|
||||
|
||||
ret = ONEWIRE_READ(priv->master->dev, buf,
|
||||
buflen + HELPBUF_MARGIN);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
crc = onewire_crc16(buf, buflen + RS_HEADER_SIZE, crc);
|
||||
reccrc = buf[RS_HEADER_SIZE + buflen] |
|
||||
buf[RS_HEADER_SIZE + buflen + 1] << 8;
|
||||
if (reccrc != (uint16_t) ~crc)
|
||||
{
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/* Memcheck should be performed, but imho CRC16 is already a good check.
|
||||
* Commit the scratchpad into the eeprom.
|
||||
*/
|
||||
|
||||
priv->helpbuf[0] = DS2XXX_COPY_SCRATCHPAD;
|
||||
priv->helpbuf[1] = buf[0];
|
||||
priv->helpbuf[2] = buf[1];
|
||||
priv->helpbuf[3] = buf[2];
|
||||
ret = ds2xxx_startcomm(priv, buf);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ONEWIRE_WRITE(priv->master->dev, priv->helpbuf, CS_HEADER_SIZE);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Impose a delay. Across all supported devices, the maximum delay is 12 ms
|
||||
* for a page.
|
||||
*/
|
||||
|
||||
up_udelay(DS2XXX_TPROG_US);
|
||||
|
||||
i2cinfo("aligned write of %zu bytes successful", buflen);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_open
|
||||
*
|
||||
* Description:
|
||||
* The open callback function.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int ds2xxx_open(FAR struct file *filep)
|
||||
{
|
||||
FAR struct ds2xxx_dev_s *priv;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(inode->i_private);
|
||||
priv = inode->i_private;
|
||||
|
||||
ret = nxrmutex_lock(&priv->master->devlock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((priv->refs + 1) == 0)
|
||||
{
|
||||
ret = -EMFILE;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->refs++;
|
||||
}
|
||||
|
||||
nxrmutex_unlock(&priv->master->devlock);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_write
|
||||
*
|
||||
* Description:
|
||||
* The write callback function. As writes into the EEPROM must be aligned
|
||||
* with the scratchpad, all nonaligned writes must first read the aligned
|
||||
* part, change some bytes and then commit the aligned part to the
|
||||
* scratchpad. This function takes care of this.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int ds2xxx_write(FAR struct file *filep, FAR const char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
FAR struct ds2xxx_dev_s *priv;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
off_t addr;
|
||||
off_t misalign;
|
||||
size_t to_write;
|
||||
size_t scratchpad_size;
|
||||
size_t buflen_save = buflen;
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(inode->i_private);
|
||||
priv = inode->i_private;
|
||||
scratchpad_size = g_eeprom_scratchpad_sizes[priv->devtype];
|
||||
|
||||
i2cwarn("writing %zu bytes to ptr %" PRIdOFF, buflen, filep->f_pos);
|
||||
|
||||
if (filep->f_pos >= g_eeprom_sizes[priv->devtype])
|
||||
{
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if ((filep->f_pos + buflen) >= g_eeprom_sizes[priv->devtype])
|
||||
{
|
||||
buflen = g_eeprom_sizes[priv->devtype] - filep->f_pos;
|
||||
buflen_save = buflen;
|
||||
}
|
||||
|
||||
ret = nxrmutex_lock(&priv->master->devlock);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* The process of writing to 1WIRE EEPROMs involves the usage
|
||||
* of the scratchpad. Firstly, the Write Scratchpad command
|
||||
* must be issued, the payload is as follows:
|
||||
* 2 bytes ADDR (Little Endian) | DATA,
|
||||
* where ADDR must be aligned to the scratchpad size
|
||||
* (DS2431's scratchpad is 8 bytes, DS2433's scratchpad is 32 bytes
|
||||
* and the DATA is maximally 8 bytes or 32 bytes, depending on the EEPROM.
|
||||
*
|
||||
* Since writes are not generally row aligned, we must read
|
||||
* the corresponding unaligned ends beforehand, modifying respective parts
|
||||
* and writing this back.
|
||||
*
|
||||
* To check the transaction has completed successfully, Read Scratchpad
|
||||
* command is issued, returning the 3-byte authorization pattern, also.
|
||||
*
|
||||
* The writing procedure is ended with issuing the Copy Scratchpad
|
||||
* command.
|
||||
*/
|
||||
|
||||
/* Calculate the misalignment in the beginning. And the starting address. */
|
||||
|
||||
misalign = (filep->f_pos) % scratchpad_size;
|
||||
addr = (filep->f_pos / scratchpad_size) * scratchpad_size;
|
||||
|
||||
/* The pointer points at the beginning of a page, decide if we skip. */
|
||||
|
||||
if (misalign == 0)
|
||||
{
|
||||
goto no_misalign_begin;
|
||||
}
|
||||
|
||||
ret = ds2xxx_read_internal(priv, addr, priv->helpbuf, scratchpad_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Fill in the misaligned data. But decide on the length of the write. */
|
||||
|
||||
if (buflen >= scratchpad_size - misalign)
|
||||
{
|
||||
to_write = scratchpad_size - misalign;
|
||||
}
|
||||
else
|
||||
{
|
||||
to_write = buflen;
|
||||
}
|
||||
|
||||
memcpy(priv->helpbuf + misalign, buffer, to_write);
|
||||
ret = ds2xxx_write_internal(priv, addr, priv->helpbuf, scratchpad_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Move all the pointers */
|
||||
|
||||
buffer += to_write;
|
||||
buflen -= to_write;
|
||||
addr += scratchpad_size;
|
||||
|
||||
no_misalign_begin:
|
||||
|
||||
/* Now loop over all aligned writes. If everything was already
|
||||
* written in the misaligned write or the next write is also misaligned,
|
||||
* this loop won't run.
|
||||
*/
|
||||
|
||||
while (buflen >= scratchpad_size)
|
||||
{
|
||||
ret = ds2xxx_write_internal(priv, addr, (uint8_t *)buffer,
|
||||
scratchpad_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
buffer += scratchpad_size;
|
||||
buflen -= scratchpad_size;
|
||||
addr += scratchpad_size;
|
||||
}
|
||||
|
||||
/* There's no rest. */
|
||||
|
||||
if (buflen == 0)
|
||||
{
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* Finish this with the last read. The read is aligned but needs to be
|
||||
* of the same size as the scratchpad.
|
||||
*/
|
||||
|
||||
ret = ds2xxx_read_internal(priv, addr, priv->helpbuf, scratchpad_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(priv->helpbuf, buffer, buflen);
|
||||
ret = ds2xxx_write_internal(priv, addr, priv->helpbuf, scratchpad_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Everything went well. Move the pointer */
|
||||
|
||||
success:
|
||||
i2cwarn("success writing %d bytes", buflen_save);
|
||||
filep->f_pos += buflen_save;
|
||||
ret = buflen_save;
|
||||
done:
|
||||
nxrmutex_unlock(&priv->master->devlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_read
|
||||
*
|
||||
* Description:
|
||||
* The read callback function. First, it checks the boundaries and then
|
||||
* calls the ds2xxx_read_internal function.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t ds2xxx_read(FAR struct file *filep, FAR char *buffer,
|
||||
size_t len)
|
||||
{
|
||||
FAR struct ds2xxx_dev_s *priv;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(inode->i_private);
|
||||
priv = inode->i_private;
|
||||
|
||||
ret = nxrmutex_lock(&priv->master->devlock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The pointer is outside the "file" */
|
||||
|
||||
if (filep->f_pos >= g_eeprom_sizes[priv->devtype])
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Trim the length */
|
||||
|
||||
if ((filep->f_pos + len) > g_eeprom_sizes[priv->devtype])
|
||||
{
|
||||
len = g_eeprom_sizes[priv->devtype] - filep->f_pos;
|
||||
}
|
||||
|
||||
ret = ds2xxx_read_internal(priv, filep->f_pos, (uint8_t *)buffer, len);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = len;
|
||||
filep->f_pos += len;
|
||||
|
||||
i2cinfo("DS2XXX read OK, curpos=%" PRIdOFF, filep->f_pos);
|
||||
|
||||
done:
|
||||
nxrmutex_unlock(&priv->master->devlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_close
|
||||
*
|
||||
* Description:
|
||||
* The close callback function.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int ds2xxx_close(FAR struct file *filep)
|
||||
{
|
||||
FAR struct ds2xxx_dev_s *priv;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(inode->i_private);
|
||||
priv = inode->i_private;
|
||||
|
||||
ret = nxrmutex_lock(&priv->master->devlock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (priv->refs == 0)
|
||||
{
|
||||
ret = -EIO;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->refs--;
|
||||
}
|
||||
|
||||
nxrmutex_unlock(&priv->master->devlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_seek
|
||||
*
|
||||
* Description:
|
||||
* The seek callback function. Practically the same as in i2c_xx24xx.c.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static off_t ds2xxx_seek(FAR struct file *filep, off_t offset, int whence)
|
||||
{
|
||||
FAR struct ds2xxx_dev_s *priv;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
int ret;
|
||||
off_t newpos;
|
||||
|
||||
DEBUGASSERT(inode->i_private);
|
||||
priv = inode->i_private;
|
||||
|
||||
ret = nxrmutex_lock(&priv->master->devlock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_CUR:
|
||||
newpos = filep->f_pos + offset;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
newpos = offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
newpos = g_eeprom_sizes[priv->devtype] + offset;
|
||||
break;
|
||||
default:
|
||||
nxrmutex_unlock(&priv->master->devlock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Opengroup.org:
|
||||
*
|
||||
* "The lseek() function shall allow the file offset to be set beyond the
|
||||
* end of the existing data in the file. If data is later written at
|
||||
* this point, subsequent reads of data in the gap shall return bytes
|
||||
* with the value 0 until data is actually written into the gap."
|
||||
*
|
||||
* We can conform to the first part, but not the second. But return -EINVAL
|
||||
* if
|
||||
*
|
||||
* "...the resulting file offset would be negative for a regular file,
|
||||
* block special file, or directory."
|
||||
*/
|
||||
|
||||
if (newpos >= 0)
|
||||
{
|
||||
filep->f_pos = newpos;
|
||||
ret = newpos;
|
||||
i2cinfo("ds2xxx_seek: newpos %" PRIdOFF, newpos);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
nxrmutex_unlock(&priv->master->devlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_ioctl
|
||||
*
|
||||
* Description:
|
||||
* The ioctl callback function. The target romcode is specified here.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int ds2xxx_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
||||
{
|
||||
FAR struct ds2xxx_dev_s *priv;
|
||||
FAR struct inode *inode = filep->f_inode;
|
||||
int ret = OK;
|
||||
|
||||
DEBUGASSERT(inode->i_private);
|
||||
priv = inode->i_private;
|
||||
|
||||
nxrmutex_lock(&priv->master->devlock);
|
||||
switch (cmd)
|
||||
{
|
||||
case ONEWIREIOC_SETROM:
|
||||
/* The issue is that unsigned long is 32bits on most architectures.
|
||||
* That means we should not pass the romcode as a value.
|
||||
*/
|
||||
|
||||
i2cinfo("Rom to be set is 0x%" PRIx64, *((uint64_t *)arg));
|
||||
ret = onewire_ioctl_setrom(priv->master, *((uint64_t *)arg));
|
||||
break;
|
||||
case ONEWIREIOC_GETFAMILYROMS:
|
||||
ret = onewire_ioctl_getfamilyroms(priv->master,
|
||||
(FAR struct onewire_availroms_s *)arg,
|
||||
g_eeprom_familycodes[priv->devtype]);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
|
||||
nxrmutex_unlock(&priv->master->devlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int ds2xxx_initialize(FAR struct onewire_dev_s *dev,
|
||||
enum ds2xxx_eeproms_e devtype, FAR char *devname)
|
||||
{
|
||||
FAR struct ds2xxx_dev_s *priv;
|
||||
|
||||
if (devtype < 0 || devtype > EEPROM_DS_COUNT)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv = kmm_zalloc(sizeof(struct ds2xxx_dev_s));
|
||||
if (priv == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->master = onewire_initialize(dev, CONFIG_1WIRE_EE_DS2XXX_MEMSONBUS);
|
||||
if (priv->master == NULL)
|
||||
{
|
||||
kmm_free(priv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* We need extra 5 bytes for the Copy Scratchpad command. */
|
||||
|
||||
priv->helpbuf = kmm_zalloc(HELPBUF_MARGIN +
|
||||
g_eeprom_scratchpad_sizes[devtype]);
|
||||
if (priv->helpbuf == NULL)
|
||||
{
|
||||
kmm_free(priv->master);
|
||||
kmm_free(priv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nxrmutex_init(&priv->master->devlock);
|
||||
priv->master->dev = dev;
|
||||
priv->devtype = devtype;
|
||||
|
||||
return register_driver(devname, &g_ds2xxx_fops, 0666, priv);
|
||||
}
|
||||
@@ -27,5 +27,10 @@ if(CONFIG_1WIRE)
|
||||
list(APPEND SRCS ds28e17.c)
|
||||
endif()
|
||||
|
||||
# Include the 1WIRE driver for DS2XXX EEPROMs
|
||||
if(CONFIG_1WIRE_EE_DS2XXX)
|
||||
list(APPEND SRCS 1wire_ds2xxx.c)
|
||||
endif()
|
||||
|
||||
target_sources(drivers PRIVATE ${SRCS})
|
||||
endif()
|
||||
|
||||
@@ -18,4 +18,27 @@ config 1WIRE_DS28E17
|
||||
---help---
|
||||
Enable support for the Maxim DS28E17 1-wire to I2C converter
|
||||
|
||||
config 1WIRE_EE_DS2XXX
|
||||
bool "Support for Maxim/Analog Devices 1wire DS2XXX EEPROM devices"
|
||||
default n
|
||||
depends on 1WIRE
|
||||
---help---
|
||||
Enable support for 1WIRE DS2XXX EEPROMs.
|
||||
The list of supported devices:
|
||||
DS2430: 32 bytes, 1 page
|
||||
DS2431: 128 bytes, 4 pages
|
||||
DS2432: 128 bytes, 4 pages
|
||||
DS2433: 512 bytes, 16 pages
|
||||
DS28E04: 512 bytes, 16 pages
|
||||
DS28E07: 128 bytes, 4 pages
|
||||
DS28EC20: 2560 bytes, 32 pages
|
||||
|
||||
if 1WIRE_EE_DS2XXX
|
||||
|
||||
config 1WIRE_EE_DS2XXX_MEMSONBUS
|
||||
int "Maximum number of DS2XXX eeproms on the same bus"
|
||||
default 10
|
||||
|
||||
endif # 1WIRE_EE_DS2XXX
|
||||
|
||||
endif # 1WIRE
|
||||
|
||||
@@ -28,6 +28,10 @@ ifeq ($(CONFIG_1WIRE_DS28E17),y)
|
||||
CSRCS += ds28e17.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_1WIRE_EE_DS2XXX),y)
|
||||
CSRCS += 1wire_ds2xxx.c
|
||||
endif
|
||||
|
||||
# Include 1wire device driver build support
|
||||
|
||||
DEPPATH += --dep-path 1wire
|
||||
|
||||
88
include/nuttx/1wire/1wire_ds2xxx.h
Normal file
88
include/nuttx/1wire/1wire_ds2xxx.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/****************************************************************************
|
||||
* include/nuttx/1wire/1wire_ds2xxx.h
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Author: Stepan Pressl <pressl.stepan@gmail.com>
|
||||
* <pressste@fel.cvut.cz>
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __INCLUDE_NUTTX_EEPROM_1WIRE_DS2XXX_H
|
||||
#define __INCLUDE_NUTTX_EEPROM_1WIRE_DS2XXX_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <sys/types.h>
|
||||
#include <nuttx/1wire/1wire.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
/* All device specific commands for the scratchpad are luckily the same
|
||||
* for all the devices.
|
||||
*/
|
||||
|
||||
#define DS2XXX_WRITE_SCRATCHPAD 0x0f
|
||||
#define DS2XXX_READ_SCRATCHPAD 0xaa
|
||||
#define DS2XXX_COPY_SCRATCHPAD 0x55
|
||||
#define DS2XXX_READ_MEMORY 0xf0
|
||||
|
||||
/* There are other commands, also. For example, the common commands
|
||||
* have a different code than specified in drivers/1wire/1wire.c.
|
||||
*/
|
||||
|
||||
#define DS2XXX_MATCH_ROM 0x55
|
||||
|
||||
/* DS28E05 has a different architecture,
|
||||
* as it does not use a scratchpad. Let's keep this device out of here.
|
||||
*/
|
||||
|
||||
enum ds2xxx_eeproms_e
|
||||
{
|
||||
EEPROM_DS2430 = 0,
|
||||
EEPROM_DS2431,
|
||||
EEPROM_DS2432,
|
||||
EEPROM_DS2433,
|
||||
EEPROM_DS28E04,
|
||||
EEPROM_DS28E07,
|
||||
EEPROM_DS28EC20,
|
||||
EEPROM_DS_COUNT
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: ds2xxx_initialize
|
||||
*
|
||||
* Description: Bind a onewire_master_s struct to this driver,
|
||||
* capable of interfacing DS2XXX 1Wire EEPROMs. The user must specify
|
||||
* the device type and also the name of the device (e.g. /dev/ds2xxx).
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int ds2xxx_initialize(FAR struct onewire_dev_s *dev,
|
||||
enum ds2xxx_eeproms_e devtype, FAR char *devname);
|
||||
|
||||
#endif /* __INCLUDE_NUTTX_EEPROM_1WIRE_DS2XXX_H */
|
||||
Reference in New Issue
Block a user