-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
drivers/at24cxxx: implement _mtd_at24cxxx_read_page #19270
drivers/at24cxxx: implement _mtd_at24cxxx_read_page #19270
Conversation
The |
I gave it a quick try. While it is true that we can read sequentially, it seems like it is not possible to read arbitrarily many bytes with that. I tested with the following code.
It works fine for the first 256 bytes, after that I only get garbage:
Since 256 is a power of two I suspect some kind of technical limitation here. So this is unfortunately not feasible for the MTD layer. The same code runs perfectly fine with the changes in this PR for reading single pages:
|
This is an interesting discovery. The I2C implementation is selected in # Select the specific implementation for `periph_i2c`
ifneq (,$(filter periph_i2c,$(USEMODULE)))
ifneq (,$(filter $(CPU_FAM),f0 f3 f7 g0 g4 l0 l4 l5 u5 wb wl))
SRC += i2c_1.c
else ifneq (,$(filter $(CPU_FAM),f1 f2 f4 l1))
SRC += i2c_2.c
else
$(error STM32 series I2C implementation not found.)
endif
endif So I tested your test function on a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea that's odd, I also always assumed that read would work across page boundaries.
What's the name of that EEPROM chip?
edit oops, didn't see there are more comments already. Looks like the EEPROM chip is not the culprit then but the i2c driver? That would be the one from sam0_common in our case.
It's a custom board with a SAML21 and an AT24CS08.
Assertions are enabled.
True, I initialized them with 0 and now instead of garbage I have 0 in buf2. So it seems like it's not read at all.
I mean ... it does. A page has a size of 16 bytes, so 16 pages are actually read successfully before the reading process seems to stop. |
With
|
bd3a74a
to
773202c
Compare
drivers/at24cxxx/at24cxxx.c
Outdated
int check = 0; | ||
|
||
while (len) { | ||
size_t clen = MIN(len, DEV_PAGE_SIZE - MOD_POW2(pos, DEV_PAGE_SIZE)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this would also work?
size_t clen = MIN(len, DEV_PAGE_SIZE - MOD_POW2(pos, DEV_PAGE_SIZE)); | |
size_t clen = MIN(len, 255); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if it is written like this, with a limited read, it works:
int at24cxxx_read_page(const at24cxxx_t *dev, uint32_t page, uint32_t offset,
void *data, size_t len)
{
size_t pos = page * DEV_PAGE_SIZE + offset;
size_t clen = MIN(len, 256 - MOD_POW2(pos, 256));
return at24cxxx_read(dev, pos, data, clen) == AT24CXXX_OK ? (int)clen : -EIO;
}
That is only a fix specific to this CPU's I2C implementation though, but it is an improvement nevertheless. We could only do it when e.g. MODULE_SAM0_COMMON_PERIPH
is defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need to subtract MOD_POW2(pos, 256)
?
Also as @fabian18 pointed out the STM32 driver also has this limitation, so it might be more common. Reading up to 256 bytes at once is certainly an improvement to just reading 16 bytes (or failing).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need to subtract MOD_POW2(pos, 256)?
Oh I don't, that was just some bad copy pasta.
Also as @fabian18 pointed out the STM32 driver also has this limitation, so it might be more common. Reading up to 256 bytes at once is certainly an improvement to just reading 16 bytes (or failing).
Other implementations may have even lower limits. Should we put a warning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other implementations may have even lower limits.
I doubt that.
I can see how some (HW) implementations would limit the transfer size to 1 byte length. I can't really see how less than 1 byte would make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt that.
I can see how some (HW) implementations would limit the transfer size to 1 byte length. I can't really see how less than 1 byte would make sense.
1 byte?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
255 = 8 bit = 1 byte
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we are reading 256 bytes, not bits? I'm confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If your length
variable is 1 byte it can hold a maximum of 8 bits which can represent a value of up to 255
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah now I got it, you are talking about I2C implementations using e.g. uint8_t
for the length, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to hotfix this, I would introduce an internal AT24CXXX_I2C_READ_MAX
which may be defined by a board and a function:
int _read_max(..., len) {
#ifdef AT24CXXX_I2C_READ_MAX
while(len) {
int l = _read(..., AT24CXXX_I2C_READ_MAX);
len -= len;
....
}
#else
_read(..., len)
#endif
}
It's not really specific to at24 though, it's a property of the I2C implementation. |
Ok so basically export something like |
773202c
to
61027ab
Compare
drivers/at24cxxx/at24cxxx.c
Outdated
data_p += clen; | ||
} | ||
else { | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not
break; | |
return -EIO; |
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have thought that we do not want the operation to be "all or nothing" and return at least bytes we successfully read. That is also the answer to your question why there is the extra function at24cxxx_read_max
: So we can return the bytes that were read instead of just returning AT24CXXX_OK
as read_page
in MTD needs to return the number of bytes read?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it's hiding the error if one happened.
Also, what are you going to do with half a transaction? In case of the MTD layer, it will just retry because it expects read_page
to return less than what was requested in case of boundary condition.
With this, in case of an error, this would just return 0 and we will spin forever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it should be the alternative that I proposed?
static int _mtd_at24cxxx_read_page(mtd_dev_t *mtd, void *dest, uint32_t page,
uint32_t offset, uint32_t size)
{
return at24cxxx_read(DEV(mtd), page * (mtd->page_size) + offset, dest, size) == AT24CXXX_OK ? (int)size : -EIO;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, what are you going to do with half a transaction?
MTD could request the second half in case something similar happens like only being able to read 256 bytes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW even when changing that, we can still input len=0
and get a return of 0
and not -EIO
, because we "successfully" read 0 bytes. If this is an issue we should forbid it I guess?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mtd_read_page()
does while (count) {
so the len = 0
case should never make it to the driver.
61027ab
to
d3f0cf5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me
d3f0cf5
to
3837536
Compare
bors merge |
19270: drivers/at24cxxx: implement _mtd_at24cxxx_read_page r=benpicco a=HendrikVE ### Contribution description The function `read_page` was missing which lead to (from a user perspective) undefined behavior on the MTD layer. ### Testing procedure Any application using MTD in conjunction with a board with an at24cxxx. Co-authored-by: Hendrik van Essen <[email protected]>
bors cancel |
Canceled. |
bors merge |
19270: drivers/at24cxxx: implement _mtd_at24cxxx_read_page r=benpicco a=HendrikVE ### Contribution description The function `read_page` was missing which lead to (from a user perspective) undefined behavior on the MTD layer. ### Testing procedure Any application using MTD in conjunction with a board with an at24cxxx. 19280: sys/shell/cmds: fix typo 'shell_cmd_grnc_udp' r=benpicco a=benpicco Co-authored-by: Hendrik van Essen <[email protected]> Co-authored-by: Benjamin Valentin <[email protected]>
Build failed (retrying...): |
19270: drivers/at24cxxx: implement _mtd_at24cxxx_read_page r=benpicco a=HendrikVE ### Contribution description The function `read_page` was missing which lead to (from a user perspective) undefined behavior on the MTD layer. ### Testing procedure Any application using MTD in conjunction with a board with an at24cxxx. Co-authored-by: Hendrik van Essen <[email protected]>
Build failed: |
bors merge |
19270: drivers/at24cxxx: implement _mtd_at24cxxx_read_page r=benpicco a=HendrikVE ### Contribution description The function `read_page` was missing which lead to (from a user perspective) undefined behavior on the MTD layer. ### Testing procedure Any application using MTD in conjunction with a board with an at24cxxx. Co-authored-by: Hendrik van Essen <[email protected]>
bors cancel |
Canceled. |
Build succeeded: |
Contribution description
The function
read_page
was missing which lead to (from a user perspective) undefined behavior on the MTD layer.Testing procedure
Any application using MTD in conjunction with a board with an at24cxxx.