I'm trying to play a song using mpg123 in a visual c++ project, I'm using a code from here but I can't find how to make sound. The code doesn't have errors, it works right but close immediately, This is the code, Thanks:
#include "stdafx.h"
#include <mpg123.h>
#define INBUFF 16384
#define OUTBUFF 32768
int _tmain(int argc, _TCHAR* argv[])
{
mpg123_handle *mh;
const char* filename;
filename="D:/Jose.mp3";
unsigned char *buffer;
size_t buffer_size;
size_t done;
int err;
int channels, encoding;
long rate;
mpg123_init();
mh = mpg123_new(NULL, &err);
buffer_size = mpg123_outblock(mh);
buffer = (unsigned char*) malloc(buffer_size * sizeof(unsigned char));
/* open the file and get the decoding format */
mpg123_open(mh, filename);
mpg123_getformat(mh, &rate, &channels, &encoding);
/* set the output format and open the output device */
int m_bits = mpg123_encsize(encoding);
int m_rate = rate;
int m_channels = channels;
/* decode and play */
for (int totalBtyes = 0 ; mpg123_read(mh, buffer, buffer_size, &done) == MPG123_OK;) {
totalBtyes += done;
}
/* clean up */
free(buffer);
mpg123_close(mh);
mpg123_delete(mh);
mpg123_exit();
return 0;
}
Related
I have try to compile a program which manage userfault-fd to collect some dirty pages on memory. In this program i have used the uffdio_writeprotect structure to survey a memory region where program try to access. I am using Ubuntu 18.04 with linux kernel version 5.14.0. But when i compile a program with the command gcc -o with_userfault -I. with_userfault.c
i still have the same errors such as: with_userfault.c:108:34: error: storage size of ‘wp’ isn’t known
struct uffdio_writeprotect wp;
with_userfault.c:114:21: error: ‘UFFDIO_WRITEPROTECT’ undeclared (first use in this function); did you mean ‘UFFDIO_REGISTER’?
if (ioctl(fd, UFFDIO_WRITEPROTECT, &wp) == -1)
I don't know how to solve this problem. Please I need your help to solve this issue because i don't know what i am suppose to do now. thanks!!!!!
this is a source code:
The header file:
#ifndef __RDTSC_H_DEFINED__
#define __RDTSC_H_DEFINED__
#if defined(__i386__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}
#elif defined(__x86_64__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#elif defined(__powerpc__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int result=0;
unsigned long int upper, lower,tmp;
__asm__ volatile(
"0: \n"
"\tmftbu %0 \n"
"\tmftb %1 \n"
"\tmftbu %2 \n"
"\tcmpw %2,%0 \n"
"\tbne 0b \n"
: "=r"(upper),"=r"(lower),"=r"(tmp)
);
result = upper;
result = result<<32;
result = result|lower;
return(result);
}
#else
#error "No tick counter is available!"
#endif
/* $RCSfile: $ $Author: kazutomo $
* $Revision: 1.6 $ $Date: 2005/04/13 18:49:58 $
*/
#endif
The source file
/*
* Example program about using userfaultfd(2) for garbage collection.
*
* This establishes a couple pages, all of which are filled from
* compressed files on disk when first accessed. For simplicity
* these are
* one file per page. Files are written at the beginning of the
* program.
*
* Later, this program demonstrates the use of write protection to
* get
* a notification on write access, analogous to using
* mprotect(!PROT_WRITE)
* and doing the bookkeeping in a SIGSEGV handler.
*
*/
#include <linux/userfaultfd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h>
#include <math.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <poll.h>
#include <pthread.h>
#include "rdtsc.h"
//#define size 102400
#define FILE_BUF 25
#define PAGE_SIZE sysconf(_SC_PAGE_SIZE)
#define n_pages 102400
//102400
#define iterations 1
pthread_t tracker;
void *tracker_task();
int i;
unsigned long main_tsc_start, main_tsc_end;
// This is doing the work in the uffd handler thread
void *tracker_task(void *data)
{
int fd = *(int *)(data);
for (;;)
{
struct uffd_msg msg;
struct pollfd pollfd;
int pollres, readret;
unsigned long addr, page_begin, whichpage;
unsigned long handler_tsc_start, handler_tsc_end;
pollfd.fd = fd;
pollfd.events = POLLIN;
pollres = poll(&pollfd, 1, -1);
if( pollres == -1 )
perror("poll");
if (pollfd.revents & POLLERR)
{
fprintf(stderr, "POLLERR on userfaultfd\n");
exit(1);
}
readret = read(fd, &msg, sizeof(msg));
if (readret == -1)
perror("read userfaultfd");
if (readret != sizeof(msg))
{
fprintf(stderr, "short read, not expected, exiting\n");
exit(1);
}
/*
* Proper sequence is important here.
*
* For the GC we expect that write-protected pages can only
* be pages already backed by physical pages.
* Regular writes into unprotected pages that come before
* reads need the page be filled.
*
* So we do the WP case first and get it out of the way.
* Then both of the other cases need the page read.
*/
if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP)
{
// send write unlock
struct uffdio_writeprotect wp;
wp.range.start = msg.arg.pagefault.address;
wp.range.len = PAGE_SIZE;
wp.mode = 0;
//printf("sending !UFFDIO_WRITEPROTECT event to
//userfaultfd\n");
if (ioctl(fd, UFFDIO_WRITEPROTECT, &wp) == -1)
perror("ioctl(UFFDIO_WRITEPROTECT)");
//continue;
}
}
printf("end\n");
return NULL;
}
int main(int argc, char *argv[])
{
unsigned long *region;
int uffd, uffd_flags, expected, t_create;
void *status;
struct uffdio_writeprotect wp;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
char *clear = "3", *drop_caches_path = "/proc/sys/vm/drop_caches";
FILE *drop_caches_file;
main_tsc_start = rdtsc();
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1)
{
perror("syscall");
exit(2);
}
uffdio_api.api = UFFD_API;
uffdio_api.features = 1;
if (ioctl(uffd, UFFDIO_API, &uffdio_api))
{
fprintf(stderr, "UFFDIO_API\n");
return 1;
}
//printf("Features: 0x%llx\n", uffdio_api.features);
if (uffdio_api.api != UFFD_API)
{
fprintf(stderr, "UFFDIO_API error %Lu\n", uffdio_api.api);
return 1;
}
/* Allocate memory that will be tracked */
region = (unsigned long *) mmap(NULL, PAGE_SIZE * n_pages, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (!region)
{
perror("mmap");
exit(2);
}
/* Force alignment of contiguous pages */
if (posix_memalign((void **)region, PAGE_SIZE, PAGE_SIZE * (n_pages - 1)))
{
fprintf(stderr, "cannot align by PAGE_SIZE %ld\n", PAGE_SIZE);
exit(1);
}
uffdio_register.range.start = (unsigned long)region;
uffdio_register.range.len = PAGE_SIZE * n_pages;
uffdio_register.mode = UFFDIO_REGISTER_MODE_WP;
//main_tsc_start = rdtsc();
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
{
perror("ioctl(UFFDIO_REGISTER)");
exit(1);
}
expected = UFFD_API_RANGE_IOCTLS;
if ((uffdio_register.ioctls & expected) != expected)
{
fprintf(stderr, "ioctl set is incorrect\n");
exit(1);
}
if( (t_create = pthread_create(&tracker, NULL, tracker_task,
&uffd)) != 0 )
{
errno = t_create;
perror("pthread_create");
}
//printf("mainline writing writable pages.\n");
for (unsigned long i = 0; i < n_pages; i++)
{
unsigned long entry = (i * PAGE_SIZE / sizeof(unsigned long)) + ((rand()%10000) % 512);//(i % 512);
region[entry] = i;
}
drop_caches_file = fopen(drop_caches_path, "w");
for (i = 0; i < iterations; i++)//
{
/* Indicate the range of pages to be write-protected */
//sync();
fwrite(clear, sizeof(clear), 1, drop_caches_file);
wp.range.start = (unsigned long long)region;
wp.range.len = PAGE_SIZE * n_pages;
wp.mode = UFFDIO_WRITEPROTECT_MODE_WP;
/* Write-protect pages */
if (ioctl(uffd, UFFDIO_WRITEPROTECT, &wp) == -1)
{
perror("ioctl(UFFDIO_WRITEPROTECT)");
exit(1);
}
/* Now try to "touch" the pages to trigger page faults and handling by the tracker thread */
for (unsigned long i = 0; i < n_pages; i++)
{
unsigned long entry = (i * PAGE_SIZE / sizeof(unsigned long)) + ((rand()%10000) % 512);//(i % 512);
region[entry] = i;
}
}
fclose(drop_caches_file);
if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_register.range)) {
fprintf(stderr, "ioctl unregister failure\n");
return 1;
}
main_tsc_end = rdtsc();
printf("Apllication TSC : %lu\n", (unsigned long) (main_tsc_end-main_tsc_start));
return 0;
}
I'm trying to run the test vector as described in BitTorrent BEP 44 test #1, but I'm not creating the same signature as they do:
305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff
1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01
Instead, the signature I create using libsodium is:
c44ad65291c2b1087218db8a43e3fa7b73cfa01b585b0ff9e6b962ed50e701a1
6065277417ff5bbae43d9b76e52129d27bf2e33e8b043ea67ace7ff91dae4d02
Using this code:
#include <string.h>
#include <stdio.h>
#include <sodium/crypto_sign.h>
// Test vector #1 from http://bittorrent.org/beps/bep_0044.html
// Using libsodium.
int main(int argc, char *argv[])
{
const char* buf = "3:seqi1e1:v12:Hello World!";
const char* sk =
"\xe0\x6d\x31\x83\xd1\x41\x59\x22\x84\x33\xed\x59\x92\x21\xb8\x0b"
"\xd0\xa5\xce\x83\x52\xe4\xbd\xf0\x26\x2f\x76\x78\x6e\xf1\xc7\x4d"
"\xb7\xe7\xa9\xfe\xa2\xc0\xeb\x26\x9d\x61\xe3\xb3\x8e\x45\x0a\x22"
"\xe7\x54\x94\x1a\xc7\x84\x79\xd6\xc5\x4e\x1f\xaf\x60\x37\x88\x1d";
unsigned char signature[crypto_sign_BYTES];
crypto_sign_detached(signature,
NULL,
(const unsigned char*) buf,
strlen(buf),
(const unsigned char*) sk);
char signed_buf[crypto_sign_BYTES * 2];
for (int i = 0; i < sizeof(signature); ++i) {
sprintf(signed_buf + i*2, "%.2x", signature[i]);
}
printf("%s\n", signed_buf);
}
Seems to be something silly I'm missing, but I just can't see it.
As explained here there appear to be (at least) two different formats for private keys. One of them is called ref10 and it is the one used by libsodium. It's composed of 32 bytes of seed concatenated with another 32 bytes of public key.
I couldn't find the name of the other format, but - as also explained in the above link - it's basically the seed hashed with sha512. More precisely
void ref10_to_lib(
unsigned char *private_key,
const unsigned char *ref10_private_key)
{
sha512(ref10_private_key, 32, private_key);
private_key[0] &= 248;
private_key[31] &= 63;
private_key[31] |= 64;
}
The BitTorrent specification uses the second format and to be able to use it, one must use the deprecated crypto_sign_edwards25519sha512batch function instead of crypto_sign_detached as such:
#include <string.h>
#include <stdio.h>
#include <sodium/crypto_sign.h>
#include <sodium/crypto_sign_edwards25519sha512batch.h>
// Test vector #1 from http://bittorrent.org/beps/bep_0044.html
// Using libsodium.
int main(int argc, char *argv[])
{
const char* buf = "3:seqi1e1:v12:Hello World!";
const char* sk =
"\xe0\x6d\x31\x83\xd1\x41\x59\x22\x84\x33\xed\x59\x92\x21\xb8\x0b"
"\xd0\xa5\xce\x83\x52\xe4\xbd\xf0\x26\x2f\x76\x78\x6e\xf1\xc7\x4d"
"\xb7\xe7\xa9\xfe\xa2\xc0\xeb\x26\x9d\x61\xe3\xb3\x8e\x45\x0a\x22"
"\xe7\x54\x94\x1a\xc7\x84\x79\xd6\xc5\x4e\x1f\xaf\x60\x37\x88\x1d";
unsigned char signature[crypto_sign_BYTES];
crypto_sign_edwards25519sha512batch(
signature,
NULL,
(const unsigned char*) buf,
strlen(buf),
(const unsigned char*) sk);
char signed_buf[crypto_sign_BYTES * 2];
for (int i = 0; i < sizeof(signature); ++i) {
sprintf(signed_buf + i*2, "%.2x", signature[i]);
}
printf("%s\n", signed_buf);
}
I am trying to get zero copy semantics working in linux using
vmsplice()/splice() but I don't see any performance improvement. This
is on linux 3.10, tried on 3.0.0 and 2.6.32. The following code tries
to do file writes, I have tried network socket writes() also, couldn't
see any improvement.
Can somebody tell what am I doing wrong ?
Has anyone gotten improvement using vmsplice()/splice() in production ?
#include <assert.h>
#include <fcntl.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <vector>
const char *filename = "Test-File";
const int block_size = 4 * 1024;
const int file_size = 4 * 1024 * 1024;
using namespace std;
int pipes[2];
vector<char *> file_data;
static int NowUsecs() {
struct timeval tv;
const int err = gettimeofday(&tv, NULL);
assert(err >= 0);
return tv.tv_sec * 1000000LL + tv.tv_usec;
}
void CreateData() {
for (int xx = 0; xx < file_size / block_size; ++xx) {
// The data buffer to fill.
char *data = NULL;
assert(posix_memalign(reinterpret_cast<void **>(&data), 4096, block_size) == 0);
file_data.emplace_back(data);
}
}
int SpliceWrite(int fd, char *buf, int buf_len) {
int len = buf_len;
struct iovec iov;
iov.iov_base = buf;
iov.iov_len = len;
while (len) {
int ret = vmsplice(pipes[1], &iov, 1, SPLICE_F_GIFT);
assert(ret >= 0);
if (!ret)
break;
len -= ret;
if (len) {
auto ptr = static_cast<char *>(iov.iov_base);
ptr += ret;
iov.iov_base = ptr;
iov.iov_len -= ret;
}
}
len = buf_len;
while (len) {
int ret = splice(pipes[0], NULL, fd, NULL, len, SPLICE_F_MOVE);
assert(ret >= 0);
if (!ret)
break;
len -= ret;
}
return 1;
}
int WriteToFile(const char *filename, bool use_splice) {
// Open and write to the file.
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int fd = open(filename, O_CREAT | O_RDWR, mode);
assert(fd >= 0);
const int start = NowUsecs();
for (int xx = 0; xx < file_size / block_size; ++xx) {
if (use_splice) {
SpliceWrite(fd, file_data[xx], block_size);
} else {
assert(write(fd, file_data[xx], block_size) == block_size);
}
}
const int time = NowUsecs() - start;
// Close file.
assert(close(fd) == 0);
return time;
}
void ValidateData() {
// Open and read from file.
const int fd = open(filename, O_RDWR);
assert(fd >= 0);
char *read_buf = (char *)malloc(block_size);
for (int xx = 0; xx < file_size / block_size; ++xx) {
assert(read(fd, read_buf, block_size) == block_size);
assert(memcmp(read_buf, file_data[xx], block_size) == 0);
}
// Close file.
assert(close(fd) == 0);
assert(unlink(filename) == 0);
}
int main(int argc, char **argv) {
auto res = pipe(pipes);
assert(res == 0);
CreateData();
const int without_splice = WriteToFile(filename, false /* use splice */);
ValidateData();
const int with_splice = WriteToFile(filename, true /* use splice */);
ValidateData();
cout << "TIME WITH SPLICE: " << with_splice << endl;
cout << "TIME WITHOUT SPLICE: " << without_splice << endl;
return 0;
}
I did a proof-of-concept some years ago where I got as 4x speedup using an optimized, specially tailored, vmsplice() code. This was measured against a generic socket/write() based solution. This blog post from natsys-lab echoes my findings. But I believe you need to have the exact right use case to get near this number.
So what are you doing wrong? Primarily I think you are measuring the wrong thing. When writing directly to a file you have 1 system call, which is write(). And you are not actually copying data (except to the kernel). When you have a buffer with data that you want to write to disk, it's not gonna get faster than that.
In you vmsplice/splice setup you are still copying you data into the kernel, but you have a total of 2 system calls vmsplice()+splice() to get it to disk. The speed being identical to write() is probably just a testament to Linux system call speed :-)
A more "fair" setup would be to write one program that read() from stdin and write() the same data to stdout. Write an identical program that simply splice() stdin into a file (or point stdout to a file when you run it). Although this setup might be too simple to really show anything.
Aside: an (undocumented?) feature of vmsplice() is that you can also use to to read data from a pipe. I used this in my old POC. It was basically just an IPC layer based on the idea of passing memory pages around using vmsplice().
Note: NowUsecs() probably overflows the int
I am trying to read and write to the AT24MAC402 EEPROM over i2c on the Cubieboard 2 using Arch Linux. I am using the i2c-dev library and i2c-tools.
Datasheet:
http://www.atmel.com/images/atmel-8807-seeprom-at24mac402-602-datasheet.pdf
I can successfully write (kind of...) to a chosen address and sequentially write many bites starting at that address. The issues are:
Cannot re-select another address to write once the first address has been selected.
Cannot point the the EEPROM to the location I wish to read from (by dummy-writing), and therefore have almost no real control over the EEPROM.
Upon looking at the datasheet (for hours on end), it looks as if I don't have as much control over the I2C communications as I may need using the i2c-dev library.. It would be great if I could just write X bits or X bytes directly to the EEPROM.
In short, I would like advice on how I can read and write properly to this EEPROM.
char buf[10];
int com_serial;
int failcount;
int i2c_init(char filename[40], int addr)
{
int file;
if ((file = open(filename,O_RDWR)) < 0)
{
printf("Failed to open the bus.");
/* ERROR HANDLING; you can check errno to see what went wrong */
com_serial=0;
exit(1);
}
if (ioctl(file,I2C_SLAVE,addr) < 0)
{
printf("Failed to acquire bus access and/or talk to slave.\n");
/* ERROR HANDLING; you can check errno to see what went wrong */
com_serial=0;
exit(1);
}
return file;
}
int main (int argc, char *argv[]) {
char read_buf[16];
char write_buf[17];
int i;
int file;
file=i2c_init("/dev/i2c-1",0x50); //dev,slavei2caddr
write_buf[0] = 0x00;
write_buf[1] = 'H';
write_buf[2] = 'i';
write_buf[3] = '!';
write(file, write_buf, 4);
//Successfully prints "Hi!" to bytes 0x00 -> 0x02
//Setting EEPROM to point to address 0xA0 to start reading (arbitrary address with known values: all 0xFF)
write_buf[0] = 0xA0;
write(file, write_buf, 1);
//Reading 1 byte from EEPROM, even though there is a '2'; 2 bytes would be '3'
read(file, read_buf, 2);
for (i=1; i<3; i++){
printf("%X", read_buf[i]);
}
//Prints out from address 0x04 to 0x05 instead of 0xA0 to 0xA1
printf("\n");
}
I did work properly using the functions from the linux/i2c-dev.h.
To test the code I get the output generated by i2cdump and put as input to i2c-stub-from-dump tool, it lets you setup one or more fake I2C chips on the i2c-stub bus based on dumps of the chips you want to emulate.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
int i2c_init(const char * i2c_device, const int chip_address)
{
int file;
if ((file = open(i2c_device, O_RDWR)) < 0) {
return -1;
}
if (ioctl(file, I2C_SLAVE, chip_address) < 0) {
close(file);
return -1;
}
return file;
}
int i2c_write(int file, const int data_address, const unsigned char * data, size_t size)
{
return i2c_smbus_write_i2c_block_data(file, data_address, size, data);
}
void i2c_read(int file, const int data_address, unsigned char * data_vector, size_t size)
{
unsigned char reg = data_address;
unsigned int i;
for(i = 0; i < size; ++i, ++reg) {
data_vector[i] = i2c_smbus_read_byte_data(file, reg);
}
}
int main(void) {
char device[] = "/dev/i2c-6";
int address = 0x50;
unsigned char buffer_before[30] = {0};
unsigned char buffer_after[30] = {0};
unsigned char data[] = "Hello World!";
int file;
file = i2c_init(device, address);
if (file > 0) {
i2c_read(file, 0x00, buffer_before, sizeof(data));
i2c_write(file, 0x00, data, sizeof(data));
i2c_read(file, 0x00, buffer_after, sizeof(data));
close (file);
}
printf("data read before write: %s\n", buffer_before);
printf("data read after write: %s\n", buffer_after);
return 0;
}
So I'm working my way through kernel driver programming, and currently I'm trying to build a simple data transfer between application and kernel driver.
I am using simple character device as a link between these two, and I have succeeded to transfer data to driver, but I can't get meaningful data back to user space.
Kernel driver looks like this:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <asm/uaccess.h> /* copy_from/to_user */
MODULE_LICENSE("GPL");
//Declarations
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);
/* Structure that declares the usual file access functions */
struct file_operations memory_fops = {
read: memory_read,
write: memory_write,
open: memory_open,
release: memory_release
};
//Default functions
module_init(memory_init);
module_exit(memory_exit);
/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char* tx_buffer;
char* rx_buffer;
int BUFFER_SIZE=64;
int actual_rx_size=0;
int memory_init(void) {
int result;
/* Registering device */
result = register_chrdev(memory_major, "move_data", &memory_fops);
if (result < 0) {
printk(
"<1>move_data: cannot obtain major number %d\n", memory_major);
return result;
}
/* Allocating memory for the buffers */
//Allocate buffers
tx_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
rx_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
//Check allocation was ok
if (!tx_buffer || !rx_buffer) {
result = -ENOMEM;
goto fail;
}
//Reset the buffers
memset(tx_buffer,0, BUFFER_SIZE);
memset(rx_buffer,0, BUFFER_SIZE);
printk("<1>Inserting memory module\n");
return 0;
fail:
memory_exit();
return result;
}
void memory_exit(void) {
/* Freeing the major number */
unregister_chrdev(memory_major, "memory");
/* Freeing buffers */
if (tx_buffer) {
kfree(tx_buffer); //Note kfree
}
if (rx_buffer) {
kfree(rx_buffer); //Note kfree
}
printk("<1>Removing memory module\n");
}
//Read function
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) {
printk("user requesting data, our buffer has (%d) \n", actual_rx_size);
/* Transfering data to user space */
int retval = copy_to_user(buf,rx_buffer,actual_rx_size);
printk("copy_to_user returned (%d)", retval);
return retval;
}
ssize_t memory_write( struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
//zero the input buffer
memset(tx_buffer,0,BUFFER_SIZE);
memset(rx_buffer,0,BUFFER_SIZE);
printk("New message from userspace - count:%d\n",count);
int retval = copy_from_user(tx_buffer,buf,count);
printk("copy_from_user returned (%d) we read [%s]\n",retval , tx_buffer);
printk("initialize rx buffer..\n");
memcpy(rx_buffer,tx_buffer, count);
printk("content of rx buffer [%s]\n", rx_buffer);
actual_rx_size = count;
return count; //inform that we read all (fixme?)
}
//Always successfull
int memory_open(struct inode *inode, struct file *filp) { return 0; }
int memory_release(struct inode *inode, struct file *filp) { return 0; }
And the userspace application is simple as well:
#include <unistd.h> //open, close | always first, defines compliance
#include <fcntl.h> //O_RDONLY
#include <stdio.h>
#include <stdlib.h> //printf
#include <string.h>
int main(int args, char *argv[])
{
int BUFFER_SIZE = 20;
char internal_buf[BUFFER_SIZE];
int to_read = 0;
memset(internal_buf,0,BUFFER_SIZE);
if (args < 3) {
printf("2 Input arguments needed\nTo read 10 bytes: \"%s read 10\" \
\nTo write string \"hello\": \"%s write hello\"\nExiting..\n", argv[0], argv[0]);
return 1;
}
//Check the operation
if (strcmp(argv[1],"write") == 0) {
printf("input lenght:%d", strlen(argv[2]));
//Make sure our write fits to the internal buffer
if(strlen(argv[2]) >= BUFFER_SIZE) {
printf("too long input string, max buffer[%d]\nExiting..", BUFFER_SIZE);
return 2;
}
printf("write op\n");
memcpy(internal_buf,argv[2], strlen(argv[2]));
printf("Writing [%s]\n", internal_buf);
FILE * filepointer;
filepointer = fopen("/dev/move_data", "w");
fwrite(internal_buf, sizeof(char) , strlen(argv[2]), filepointer);
fclose(filepointer);
} else if (strcmp(argv[1],"read") == 0) {
printf("read op\n");
to_read = atoi(argv[2]);
FILE * filepointer;
filepointer = fopen("/dev/move_data", "r");
int retval = fread(internal_buf, sizeof(char) , to_read, filepointer);
fclose(filepointer);
printf("Read %d bytes from driver string[%s]\n", retval, internal_buf);
} else {
printf("first argument has to be 'read' or 'write'\nExiting..\n");
return 1;
}
return 0;
}
When I execute my application, this is what happens:
./rw write "testing testing"
kernel side:
[ 2696.607586] New message from userspace - count:15
[ 2696.607591] copy_from_user returned (0) we read [testing testing]
[ 2696.607593] initialize rx buffer..
[ 2696.607594] content of rx buffer [testing testing]
So all look correct. But when I try to read:
./rw read 15
read op
Read 0 bytes from driver string[]
Kernel
[ 617.096521] user requesting data, our buffer has (15)
[ 575.797668] copy_to_user returned (0)
[ 617.096528] copy_to_user returned (0)
I guess it's quite simple what I'm doing wrong, since if I don't return 0, I can get some data back, but for example if I read with cat, it will continue looping endlessly.
I would like to understand what mistakes I have made in my thinking.
Is there a way that kernel driver would just spit out it's buffer, and then return 0, so that I wouldn't have to build some protocol there in between to take care of how much data has been read etc.
Thanks for your suggestions!
Edit: corrected the printk statement in memory_write function, and added memory_read function trace
Your read function always returns 0 because you are returning retval, and not the count of bytes read. As long as the copy_to_user() call always succeeds, retval will always be 0. Instead, as long as copy_to_user() succeeds, you should return the number of bytes actually written to user space. This documentation states that copy_to_user() returns the total number of bytes that it was unable to copy.
As an aside, you are ignoring the value of count. It is very possible that the user is requesting less data than you have available in your buffer. You should never ignore count.
Now you have the problem where your function never returns a 0. Returning a 0 is important because is tells the user application that there is no more data available for reading and the user application should close the device file.
You need to keep track in your driver how many bytes have been read vs. how many bytes have been written. This may be implemented using your actual_rx_size.
Try this:
//Read function
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) {
ssize_t bytes;
if (actual_rx_size < count)
bytes = actual_rx_size;
else
bytes = count;
printk("user requesting data, our buffer has (%d) \n", actual_rx_size);
/* Check to see if there is data to transfer */
if (bytes == 0)
return 0;
/* Transfering data to user space */
int retval = copy_to_user(buf,rx_buffer,bytes);
if (retval) {
printk("copy_to_user() could not copy %d bytes.\n", retval);
return -EFAULT;
} else {
printk("copy_to_user() succeeded!\n");
actual_rx_size -= bytes;
return bytes;
}
}