I am referring to the code in this answer.
I added error handling though.
The open() erase and the read() all perform without error and the 20 bytes that I read all are 0xff.
However, when trying to write the 20 bytes from the data[] array, I get an EINVAL errorcode from the write() function.
What could be the cause of the problem? I did erase the memory before trying to write...
I have seen your original post.
I have the same problem recently, and I found that the write size is important.
mtd_info_t(struct mtd_info_user) have a variable named writesize
(reference: https://elixir.bootlin.com/linux/v3.2/source/include/mtd/mtd-abi.h#L125)
struct mtd_info_user {
__u8 type;
__u32 flags;
__u32 size; /* Total size of the MTD */
__u32 erasesize;
__u32 writesize;
__u32 oobsize; /* Amount of OOB data per block (e.g. 16) */
__u64 padding; /* Old obsolete field; do not use */
};
when write to the mtd, should notice writesize
#include <fcntl.h>
#include <mtd/mtd-user.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
int main(){
mtd_info_t mtd_info; // the MTD structure
erase_info_t ei; // the erase block structure
int i;
unsigned char read_buf[20] = {0x00}; // empty array for reading
int fd = open("/dev/mtd0", O_RDWR); // open the mtd device for reading and
// writing. Note you want mtd0 not mtdblock0
// also you probably need to open permissions
// to the dev (sudo chmod 777 /dev/mtd0)
ioctl(fd, MEMGETINFO, &mtd_info); // get the device info
// dump it for a sanity check, should match what's in /proc/mtd
printf("MTD Type: %x\nMTD total size: %x(hex) bytes\nMTD erase size: %x(hex) bytes\nMTD write size: %x(hex) bytes\n",
mtd_info.type, mtd_info.size, mtd_info.erasesize, mtd_info.writesize);
ei.length = mtd_info.erasesize; //set the erase block size
for(ei.start = 0; ei.start < mtd_info.size; ei.start += ei.length)
{
ioctl(fd, MEMUNLOCK, &ei);
// printf("Eraseing Block %#x\n", ei.start); // show the blocks erasing
// warning, this prints a lot!
ioctl(fd, MEMERASE, &ei);
}
lseek(fd, 0, SEEK_SET); // go to the first block
read(fd, read_buf, sizeof(read_buf)); // read 20 bytes
// sanity check, should be all 0xFF if erase worked
for(i = 0; i<20; i++)
printf("buf[%d] = 0x%02x\n", i, (unsigned int)read_buf[i]);
/**********************************************************
* important part! *
* notice the size of data array is mtd_info.writesize *
**********************************************************/
uint32_t write_size = mtd_info.writesize;
unsigned char data[write_size];//write 0
bzero(data, write_size);
lseek(fd, 0, SEEK_SET); // go back to first block's start
write(fd, data, sizeof(data)); // write our message
lseek(fd, 0, SEEK_SET); // go back to first block's start
read(fd, read_buf, sizeof(read_buf));// read the data
// sanity check, now you see the message we wrote!
for(i = 0; i<20; i++)
printf("buf[%d] = 0x%02x\n", i, (unsigned int)read_buf[i]);
close(fd);
return 0;
}
Hope it can help
From ./drivers/mtd/nand/nand_base.c
#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
This is the check performed by driver:
/* Reject writes, which are not page aligned */
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
pr_notice("%s: attempt to write non page aligned data\n",__func__);
return -EINVAL;
}
Both the address you are starting the write and and length of the buffer you are writing must satisfy the macro condition(to be multiple of the subpage size).
Related
I'm currently trying to send raw binary data in the format of decimal to an external device over serial. I currently have the data in a buffer array but would like it in a structure like this:
struct packetData{
uint8_t sync1;
uint8_t sync2;
uint16_t messageId;
uint16_t dataWordCount;
uint16_t flags;
uint16_t checksum;
};
I'm also using 9600 baud, and have all the termios settings set using cfmakeraw and I'm currently writing using:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int flags = O_RDWR | O_NOCTTY | O_NDELAY;
fd = open(device, flags);
uint16_t buf_tx[BUFFER_SIZE] = {255,129,191,0,2057,0};
if(fd == -1){
printf("\n Failed to open port! ");
return -1;
}
tcgetattr(fd, &tty); //Get the current attributes of the Serial port
cfmakeraw(&tty);
cfsetispeed(&tty, B9600); //Set read speed as 9600 baud
cfsetospeed(&tty, B9600); //Set write speed as 9600 baud
if((tcsetattr(fd, TCSANOW, &tty)) != 0){
printf("Error! Can't set attributes.\n");
return -1;
}
else{
printf("Connection successful! \n");
}
while(x < 1000){
memset(buf_tx, 0, sizeof(buf_tx));
tcflush(fd, TCOFLUSH);
if(y < 5){
if(write(fd, buf_tx, 5) == -1){
printf("\n");
printf("Error>>: %s\n",strerror(errno));
y++;
}
}
tcflush(fd, TCIOFLUSH);
usleep(1000);
x++;
}
This code isnt the full code, just the setup/write parts so no need to worry about its syntax. if possible it would be nice not to have that buffer array and just use the struct directly, but I'll take what I can get.
It seems you have the serial port opening more or less in hand. I prefer to set the termios member components explicitly myself, but cfmakeraw() is perfectly fine too.
What you should consider, is having a separate function to send one or more of those structures at a time. For example,
int write_all(const int fd, const void *buf, const size_t len)
{
const char *data = buf;
size_t written = 0;
ssize_t n;
while (written < len) {
n = write(fd, data + written, len - written);
if (n > 0) {
written += n;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Error; n == -1, so errno is already set. */
return -1;
}
}
/* Success. */
return 0;
}
The function will return 0 if all data was successfully written, and -1 with errno set if an error occurs.
To send a struct packetData pkt; just use write_all(fd, &pkt, sizeof pkt).
To send a full array struct packetData pkts[5]; use write_all(fd, pkts, sizeof pkts).
To send n packets starting at pkts[i], use write_all(fd, pkts + i, n * sizeof pkts[0]).
However, you do not want to use tcflush(). It does not do what you think it does; it actually just discards data.
Instead, to ensure that the data you have written has been transmitted, you need to use tcdrain(fd).
I recommend against adding tcdrain(fd) at the end of write_all() function, because it blocks, pauses the program, until the data has been transmitted. This means that you should only use tcdrain() before you do something that requires the other end has received the transmission; for example before trying to read the response.
However, if this is a query-response interface, and you do intend to also read from the serial device, you should set tty.c_cc[VMIN] and tty.c_cc[VTIME] to reflect how you intend to use the interface. I prefer asynchronous full-duplex operation, but that requires select()/poll() handling. For half-duplex, with these exact structures only, you can use tty.c_cc[VMIN] = sizeof (struct packetData) with say tty.c_cc[VTIME] = 30, which causes read() to try and wait until a full structure is available, but at most 30 deciseconds (3.0 seconds). Something like tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 1; is more common; that causes read() to return a short count (even 0!) if there is no additional data received within a decisecond (0.1 seconds). Then, the receive function could be along the following lines:
int read_all(const int fd, void *buf, const size_t len)
{
char *const ptr = buf;
size_t have = 0;
ssize_t n;
/* This function is to be used with half-duplex query-response protocol,
so make sure we have transmitted everything before trying to
receive a response. Also assumes c_cc[VTIME] is properly set for
both the first byte of the response, and interbyte response interval
in deciseconds. */
tcdrain(fd);
while (have < len) {
n = read(fd, ptr + have, len - have);
if (n > 0) {
have += n;
} else
if (n == 0) {
/* Timeout or disconnect */
errno = ETIMEDOUT;
return -1;
} else
if (n != -1) {
/* C library bug, should never occur */
errno = EIO;
return -1;
} else {
/* Read error; errno set by read(). */
return -1;
}
}
/* Success; no errors. */
return 0;
}
If this returns -1 with errno == ETIMEDOUT, the other side took too long to answer. There may be remainder of the late response in the buffer, which you can discard with tcflush(TCIFLUSH) (or with tcflush(TCIOFLUSH), which also discards any written data not yet transmitted). Synchronization in this case is a bit difficult, because the above read_all() function doesn't return how many bytes it received (and therefore how many bytes to discard of a partial structure).
Sometimes the interface used always returns the number of bytes, but also sets errno (to 0 if no error occurred, and a nonzero error constant otherwise). That would be better for a query-response interface read and write functions, but many programmers find this use case "odd", even though it is perfectly okay by POSIX.1 standard (which is the relevant standard here).
I slightly modified the demo taken from ALSA Project website in order to test it on my laptop's sound card (Intel PCH ALC3227 Analog, Ubuntu 18.04), which requires 2 channels and 16 bit integers. I also doubled the latency (1 s), switched off resampling and made the demo lasts longer. This is the code (runtime error checking not pasted for sake of synthesis)
#include <alsa/asoundlib.h>
#include <stdlib.h>
static char *device = "hw:1,0"; /* playback device */
snd_output_t *output = NULL;
unsigned char buffer[16*1024]; /* some random data */
int main(void) {
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
for (i = 0; i < sizeof(buffer); i++)
buffer[i] = (unsigned char) (rand() & 0xff);
snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)
snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 48000, 0, 1E6);
// Print actual buffer size
snd_pcm_hw_params_t *hw_params;
snd_pcm_hw_params_malloc(&hw_params);
snd_pcm_hw_params_current(handle, hw_params);
snd_pcm_uframes_t bufferSize;
snd_pcm_hw_params_get_buffer_size(hw_params, &bufferSize);
printf("ALSA buffer size = %li\n", bufferSize);
// playback
for (i = 0; i < 256; ++i) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer) / 4);
if (frames < 0)
frames = snd_pcm_recover(handle, (int) frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror((int) frames));
break;
}
if (frames > 0 && frames < (long) sizeof(buffer) / 4)
printf("Short write (expected %li, wrote %li)\n",
(long) sizeof(buffer) / 4, frames);
}
snd_pcm_hw_params_free(hw_params);
snd_pcm_close(handle);
return (0);
}
Audio works but could someone explain me why I sometimes get output like the following
ALSA buffer size = 16384
Short write (expected 4096, wrote 9)
Short write (expected 4096, wrote 4080)
indicating that less frames than expected have been written by snd_pcm_writei? According to the ALSA docs, I understand that a signal has to be occurred, but I don't get the reason and which signal is.
I also tried to halve the buffer's size, but the result is pretty the same.
A short read is reported when an error happens, but some frames were already written successfully.
You are supposed to call the same function again, with the remaining buffer; if the error was not transient, it will be reported then.
(This example code is wrong; it just ignores that the remaining part of the buffer was not written.)
I am trying to figure out a nice solution to reading serial data, and what to do when a read() is done but it contains an incomplete message.
The expected messages between devices have a defined start and end byte so its easy to see when a message starts and ends.
I can open a serial port fine and read from the serial port. But I am encountering the computer is reading faster than data coming through and I get an incomplete message.
For this example, lets say the message expected is
0x10 0xFF 0xFF 0xFF 0xFF 0x11
With 0x10 the start, 0x11 the end, and 0xFF is the data bytes
I am new to C so I may be missing something obvious,
My current solution
int main() {
/* Ommited serial port opening and checking*/
char read_buffer[80];
char message_buffer[80];
int message_buffer_index = 0;
int start_index = -1;
int end_index = -1;
int read_bytes;
read_bytes = read(serial_port, read_buffer, sizeof(read_buffer) - 1);
/* Now lets say read_bytes returns 3 and read buffer is {0x10, 0xFF, 0xFF} */
/* What should I do with the read_buffer? Currently appending to message buffer*/
memcpy(&message_buffer[message_buffer_index], &read_buffer[0], read_bytes);
/* Now check the message buffer for a full message */
for (int i = 0; i < 80; i++) {
if (message_buffer[i] = 0x10) {
start_index = i;
continue;
}
if (message_buffer[i] = 0x11) {
end_index = i;
}
if (start_index != -1 && end_index != -1) {
/* Found a message, do something with it, not super important here */
process_message();
/* Now how to erase the full message from the
buffer and push any non processed data to the
front? */
remove_message();
}
}
}
int process_message();
int remove_message();
To minimize the overhead of making many read() syscalls of small byte counts (e.g. the misguided solution of reading a byte at a time), use an intermediate buffer in your code.
The read() of the serial terminal should be in blocking mode to avoid a return code of zero bytes.
#define BLEN 1024
unsigned char rbuf[BLEN];
unsigned char *rp = &rbuf[BLEN];
int bufcnt = 0;
/* get a byte from intermediate buffer of serial terminal */
static unsigned char getbyte(void)
{
if ((rp - rbuf) >= bufcnt) {
/* buffer needs refill */
bufcnt = read(fd, rbuf, BLEN);
if (bufcnt <= 0) {
/* report error, then abort */
}
rp = rbuf;
}
return *rp++;
}
For proper termios initialization code for the serial terminal, see this answer. You should increase the VMIN parameter to something closer to the BLEN value or at least the length of longest expected message, and a VTIME of 1.
Now you can conveniently access the received data a byte at a time with minimal performance penalty.
#define MLEN 1024 /* choose appropriate value for message protocol */
int main()
{
unsigned char mesg[MLEN];
...
while (1) {
while (getbyte() != 0x10)
/* discard data until start found */ ;
length = 0;
while ((mesg[length] = getbyte()) != 0x11) {
/* accumulate data until end found */
length++;
}
/* process the message */
...
} /* loop for next message */
...
}
Note that your detection for a message frame is not robust.
If the data is binary and therefore can use the same values as these start and end bytes, then this parsing of the received data is prone to misaligned message frames.
See this answer for a description of a proper alogrithm.
You need circular buffer. Place data in the buffer and the process takes them when for example there is enough data or in any convenient moment.
Wikipedia has excellent article about it https://en.wikipedia.org/wiki/Circular_buffer
Better use stdio for reading, something like this:
FILE *fp = fdopen(serial_port, "r");
while (blabla) {
while (fgetc(fp) != 0x10)
; // wait until start
while ((c = fgetc(fp)) != 0x11)
message_buffer[message_buffer_index++] = c;
// here you have a complete message
}
Insert checks for EOF and errors if needed
I have this program which reads data from LBA (logical block address), but everytime whatever the LBA number i provide, it gives the same output.
How do i validate it?
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
//#include "common.h"
typedef unsigned long long int var64;
int getSectorSize(int handle)
{
int sectorSize = 0;
//get the physical sector size of the disk
if (ioctl(handle, BLKSSZGET, §orSize)) {
printf("getSectorSize: Reading physical sector size failed.\n");
sectorSize = 512;
}
return sectorSize;
}
var64 readLBA(int handle, var64 lba, void* buf, var64 bytes)
{
int ret = 0;
int sectorSize = getSectorSize(handle);
var64 offset = lba * sectorSize;
printf("readFromLBA: entered.\n");
lseek64(handle, offset, SEEK_SET);
ret = read(handle, buf, bytes);
if(ret != bytes) {
printf("read LBA: read failed.\n");
return -1;
}
printf("read LBA: retval: %lld.\n", ret);
return ret;
}
int main()
{
int sectorSize, fd;
char buff[100];
printf("Calling getSectorSize\n");
fd = open("/dev/sda1", O_RDONLY);
if(fd == -1)
{
printf("open /dev/sda1 failed");
exit(1);
}
sectorSize = getSectorSize(fd);
printf("Sector size = %u\n", sectorSize);
memset(buff, 0, sizeof(buff));
readLBA(fd, 1, buff, 2); // if i put the 2nd arg as -12378 gives same answer
}
Here is the output:
sles10-sp3:~ # gcc getSectorSizeMain.c
getSectorSizeMain.c: In function ‘main’:
getSectorSizeMain.c:75: warning: incompatible implicit declaration of built-in function ‘memset’
sles10-sp3:~ # ./a.out
Calling getSectorSize
Sector size = 512
read LBA: entered.
read LBA: retval: 8589934594. // This is always constant, how to validate? If i tell to read an invalid LBA number like -123456 the answer remains same. How to validate?
retval doesn't contain the data you are interested in, but the count of bytes read() has stored into your buffer, so it's natural it always contains the same value. But in your test output you try to print it using "%lld" (long long int), even when it's just a plain int, so printf will combine its value with whatever it finds next to it on the stack (notice that 8589934594==0x200000002 - the last digit is your value, the first one propably garbage).
The data you want to check/use/whatever are inside the array buff.
I have a requirement of waiting using "read" until a buffer is full on an audio codec device. For make it easier, lets take similar example of:
fd= read(fileno(stdin), &buf, 10);
How can I return from the read when I type 10 characters in stdin? (I hope if this is success, I can wait on codec until specified bytes of data is arrived).
The above example needs an "Enter Key" from console, where as I want "read" to unblock only when desired bytes of data is arrived.
EDIT: Requirement is waiting using a single "read" till specified bytes are arrived.
How about this:
#define BUFFER_SIZE 10
char buffer[BUFFER_SIZE];
/* ... */
size_t total_read = 0;
size_t total_left = BUFFER_SIZE; /* The total size of the buffer */
char *buffer_pointer = buffer; /* buffer is where to store the data */
while (total_left > 0)
{
ssize_t current = read(STDIN_FILENO, buffer_pointer, total_left);
if (current <= 0)
{
/* Error or end of file */
if (current < 0)
perror("read"); /* Error! */
break;
}
else
{
total_read += current; /* We have read some more data */
total_left -= current; /* Less data left to read */
buffer_pointer += current; /* So we don't read over already read data */
}
}
printf("Received %ld characters\n", total_read);
for (unsigned int i = 0; i < total_read; i++)
printf("Character #%d: '%c'\n", i, buffer[i]);
Beware that this will block your program until all data is read. The amount of characters read may be less than everything, because there might be an error or the user pressed CTRL-D (end of file).
Also note that the STDIN_FILE file-descriptor is most likely connected to a tty, which means it might be buffered, so doesn't return data until newline, and that you might have to make the tty unbuffered.
Edit
To make sure the tty connected to stdin is unbuffered, use the following code:
#include <termios.h>
/* ... */
/* Somewhere before reading from stdin... */
struct termios tty_settings;
tcgetattr(STDIN_FILENO, &tty_settings);
tty_settings.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &tty_settings);
For more information about the tcsetattr function and the ICANON flag, check the manual page for tcsetattr.
do {ioctl(fd_port, FIONREAD, &bytes);} while (bytes < size);
May help, before issuing a read().