I want to get an dual-way voice talk program using SDL_Audio and libsoup. I create an server and an client to build it.
The server create a SoupServer and add an websocket handler. The client create a SoupSession and connect the server.
then both server and client can get a SoupWebsocketConnection. From here, they almost do the same thing: init the SDL_Audio and open a playback and a capture device to do capturing and playing work, and send samples to the connectiong and receive the message from the connection. As document said, the play and capture code will execute in another thread.
The program works good on MacOS and intel-based Linux box. But When I run it on an Arm-based Linux box (rockpi4a), The voice I heard on the Macos/intel-based Linux was delayed about 1.8 seconds.
I think somewhere was cached/queued but I don't known how to change them. Maybe the arm-based box's performance was low, and SDL_Audio or SoupWebsocketConnection increase the queue's size automatic?
the core code was here:
#include <stdio.h>
#include <libsoup/soup.h>
#include <opus.h>
#include <SDL2/SDL.h>
GAsyncQueue *queue = NULL;
OpusEncoder *encoder = NULL;
OpusDecoder *decoder = NULL;
SDL_AudioSpec spec;
SDL_AudioDeviceID playback_id;
SDL_AudioDeviceID capture_id;
void WsMessage(SoupWebsocketConnection *connection,
gint type,
GBytes *message,
gpointer user_data)
{
g_async_queue_lock (queue);
if (g_async_queue_length_unlocked (queue) > 1000)
g_bytes_unref (g_async_queue_pop_unlocked (queue));
g_async_queue_push_unlocked (queue,
message);
g_bytes_ref (message); g_async_queue_unlock (queue);
}
void WsClose (SoupWebsocketConnection *connection,
gpointer user_data)
{
SDL_CloseAudioDevice(playback_id);
puts ("关闭放音设备");
SDL_CloseAudioDevice(capture_id);
puts ("关闭采音设备");
opus_encoder_destroy (encoder);
opus_decoder_destroy (decoder);
while (g_async_queue_length(queue))
g_bytes_unref (g_async_queue_pop (queue));
g_async_queue_unref (queue);
g_object_unref (connection);
SDL_Quit();
}
void PlayAudio (void *userdata,
Uint8 *stream,
int len)
{
SDL_memset (stream, '\0', len);
GBytes *buffer = NULL;
g_async_queue_lock(queue);
if (g_async_queue_length_unlocked (queue))
buffer = g_async_queue_pop_unlocked (queue);
g_async_queue_unlock(queue);
if (buffer)
{
float *pcm = (float *)malloc(len);
int size = opus_decode_float (decoder,
g_bytes_get_data (buffer,
NULL),
g_bytes_get_size (buffer),
pcm,
spec.samples,
0);
if (size > 0)
SDL_memcpy (stream, pcm,
len);
else
{
fprintf (stderr,
"解码结果[%d]。\n",
size);
fprintf (stderr,
" %d: Ok\n",
OPUS_OK);
fprintf (stderr,
"%d: Bad arg\n",
OPUS_BAD_ARG);
fprintf (stderr,
"%d: Buffer too small\n",
OPUS_BUFFER_TOO_SMALL);
fprintf (stderr,
"%d: Internal error\n",
OPUS_INTERNAL_ERROR);
fprintf (stderr,
"%d: Unimplemented\n",
OPUS_UNIMPLEMENTED);
fprintf (stderr,
"%d: Invalid state\n",
OPUS_INVALID_STATE);
fprintf (stderr,
"%d: Alloc fail\n",
OPUS_ALLOC_FAIL);
}
free (pcm);
g_bytes_unref (buffer);
}
}
void CaptAudio (void *userdata,
Uint8 *stream,
int len)
{
SoupWebsocketConnection *connection = (SoupWebsocketConnection *)userdata;
unsigned char *encoded = malloc (len);
gsize size = opus_encode_float (encoder,
(float *)stream,
spec.samples,
encoded,
len);
if (size > 0)
{
if (SOUP_WEBSOCKET_STATE_OPEN == soup_websocket_connection_get_state (connection))
{
soup_websocket_connection_send_binary (connection,
encoded,
size);
}
}
else
{
fprintf (stderr,
"解码结果[%d]。\n",
(int)size); fprintf (stderr,
" %d: Ok\n",
OPUS_OK);
fprintf (stderr,
"%d: Bad arg\n",
OPUS_BAD_ARG);
fprintf (stderr,
"%d: Buffer too small\n",
OPUS_BUFFER_TOO_SMALL);
fprintf (stderr,
"%d: Internal error\n",
OPUS_INTERNAL_ERROR);
fprintf (stderr,
"%d: Unimplemented\n",
OPUS_UNIMPLEMENTED);
fprintf (stderr,
"%d: Invalid state\n",
OPUS_INVALID_STATE);
fprintf (stderr,
"%d: Alloc fail\n",
OPUS_ALLOC_FAIL);
}
free (encoded);
}
void ConnectionInit (SoupWebsocketConnection *connection,
const char *playback_device,
const char *capture_device)
{
SDL_Init (SDL_INIT_AUDIO);
SDL_zero(spec);
spec.freq = 16000;
spec.format = AUDIO_F32SYS;
spec.channels = 1;
spec.samples = spec.freq / 1000 * 20;
spec.callback = PlayAudio;
playback_id = SDL_OpenAudioDevice(playback_device,
FALSE,
&spec,
NULL,
0);
if (!playback_id)
{
fprintf (stderr,
"无法打开放音设备[%s]:%s\n",
playback_device,
SDL_GetError());
goto err_open_playback;
}
spec.callback = CaptAudio;
spec.userdata = connection;
capture_id = SDL_OpenAudioDevice (capture_device,
SDL_TRUE,
&spec,
NULL,
0);
if (!capture_id)
{
fprintf (stderr,
"无法打开采音设备[%s]:%s\n",
capture_device,
SDL_GetError());
goto err_open_capture;
}
encoder = opus_encoder_create(spec.freq,
spec.channels,
OPUS_APPLICATION_VOIP,
NULL);
decoder = opus_decoder_create(spec.freq,
spec.channels,
NULL);
g_signal_connect (connection,
"message",
G_CALLBACK (WsMessage),
connection);
g_signal_connect (connection,
"closed",
G_CALLBACK (WsClose),
NULL);
queue = g_async_queue_new();
SDL_PauseAudioDevice (playback_id,
SDL_FALSE);
puts("打开放音设备。");
SDL_PauseAudioDevice(capture_id,
SDL_FALSE);
puts("打开采音设备。");
g_object_ref (connection);
return;
err_open_capture:
SDL_CloseAudioDevice(playback_id);
err_open_playback:
err_connection:
return;
}
Related
I'm using popen to execute a command and read the output. I'm setting the file descriptor to non-blocking mode so that I can put in my own timeout, as follows:
auto stream = popen(cmd.c_str(), "r");
int fd = fileno(stream);
int flags = fcntl(fd, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
while(!feof(stream)) {
if(fgets(buffer, MAX_BUF, stream) != NULL) {
// do something with buffer...
}
sleep(10);
}
pclose(stream);
This works just fine, except that fgets keeps returning NULL, until the program has finished executing, at which time it returns all the output as expected.
In other words, even if the program immediately outputs some text and a newline to the stdout, my loop doesn't read it immediately; it only sees it later.
In the documentation for popen I see:
Note that output popen() streams are block buffered by default.
I've tried a few things to turn off buffering (ex. setvbuf(stream, NULL, _IONBF, 0)) , but so far no luck.
How do I turn off buffering so that I can read the output in real-time?
Thank you!
A solution based on something like select() would be more accurate and flexible. Try this :
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/select.h>
void read_cmd(const char *cmd)
{
FILE *stream;
int fd;
int flags;
char buffer[1024];
fd_set fdset;
struct timeval timeout;
int rc;
int eof;
stream = popen(cmd, "r");
fd = fileno(stream);
eof = 0;
while(!eof) {
timeout.tv_sec = 10; // 10 seconds
timeout.tv_usec = 0;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
rc = select(fd + 1, &fdset, 0, 0, &timeout);
switch(rc) {
case -1: {
// Error
if (errno != EINTR) {
fprintf(stderr, "select(): error '%m' (%d)\n", errno);
}
return;
}
break;
case 0: {
// Timeout
printf("Timeout\n");
}
break;
case 1: {
// Something to read
rc = read(fd, buffer, sizeof(buffer) - 1);
if (rc > 0) {
buffer[rc] = '\0';
printf("%s", buffer);
fflush(stdout);
}
if (rc < 0) {
fprintf(stderr, "read(): error '%m' (%d)\n", errno);
eof = 1;
}
if (0 == rc) {
// End of file
eof = 1;
}
}
break;
} // End switch
} // End while
pclose(stream);
}
int main(int ac, char *av[])
{
read_cmd(av[1]);
return 0;
} // main
I am trying to run a code for finding maximum element of array using parallel reduction in Cuda
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
/* a is the array that holds the values and c is the array used to store the maximum in a block */
cudaError_t reduce_max(int *a,int *c,int size);
/*The kernel that performs the reduction */
__global__ void global_max(int *d_c, int * d_a)
{
int myId=threadIdx.x+blockDim.x*blockIdx.x;
int tid=threadIdx.x;
for(int s=(blockDim.x)/2; s>0; s>>1)
{
if(tid<s)
{
d_a[myId]=max(d_a[myId],d_a[myId+s]);
}
__syncthreads();
}
if(tid==0)
{
d_c[blockIdx.x]=d_a[myId];
}
}
int main()
{
const int arraySize = 1024;
int i;
int a[arraySize];
for(i=0;i<arraySize;i++)
{
a[i]=i;
}
int c[arraySize];
cudaError_t cudaStatus = reduce_max(a,c,arraySize);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "The required operation failed");
return 1;
}
cudaStatus = cudaThreadExit();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaThreadExit failed!");
return 1;
}
return 0;
}
// Helper function for using CUDA to add vectors in parallel.
cudaError_t reduce_max(int *a,int *c,int size)
{
int *dev_a = 0;
int *dev_c = 0;
/*
dev_a and dev_c are the arrays on the device
*/
cudaError_t cudaStatus;
const dim3 blockSize(64,1,1);
const dim3 gridSize(size/blockSize.x,1,1);
// Choose which GPU to run on, change this on a multi-GPU system.
cudaStatus = cudaSetDevice(0);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
goto Error;
}
/*Allocating the memory on the device */
cudaStatus = cudaMalloc((void**)&dev_c, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
/*Copying array from host to device */
cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
/*Calling the kernel */
global_max<<<gridSize,blockSize>>>(dev_c, dev_a);
cudaStatus = cudaThreadSynchronize();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaThreadSynchronize returned error code %d\n", cudaStatus);
goto Error;
}
// Copy output vector from GPU buffer to host memory.
cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
Error:
cudaFree(dev_c);
cudaFree(dev_a);
return cudaStatus;
}
However on executing the above code I get the error:
cudaThreadSynchronize returned error code 6.
I am unable to figure out the problem.
Your code will run forever. As a result you are hitting a timeout.
This line is broken, and your compiler should be throwing a warning:
for(int s=(blockDim.x)/2; s>0; s>>1)
s>>1 does not modify the s variable. I'm pretty sure you meant s>>=1 which modifies s. Without modification of s, your loop runs forever and as a result you hit a kernel timeout.
Do this instead:
for(int s=(blockDim.x)/2; s>0; s>>=1)
I have the following codes..
server.c
#include <stdio.h>
#include "./../linux.h"
#include "./tcp.h"
#include <pthread.h>
static int clients = 0;
static int* memptr = NULL;
void serve(void*);
int* push(int* memptr, int nsfd) {
clients++;
if (clients == 1)
memptr = (int*)malloc(sizeof(int) * clients);
else
memptr = (int*)realloc(memptr, sizeof(int) * clients);
memptr[clients - 1] = nsfd;
return memptr;
}
int main(int argc, char** argv) {
pthread_t thread[2];
int threadCount = 0;
if (argc != 3){
printf("\nUsage: ./server port_number maximum_clients\n");
return 1;
}
static struct sockaddr_in sock, sock_client;
int len, new_sock_fd;
int sock_fd = socket(PF_INET, SOCK_STREAM, 0);
if (sock_fd == -1){
perror("socket");
exit(1);
}
sock.sin_family = PF_INET;
sock.sin_port = htons(atoi(argv[1]));
sock.sin_addr.s_addr = inet_addr("0.0.0.0");
len = sizeof(sock);
if ( bind(sock_fd, (struct sockaddr *)&sock, len) == -1){
perror("bind");
exit(1);
}
if ( listen(sock_fd, atoi(argv[2])) == -1){
perror("listen");
exit(1);
}
while(1) {
new_sock_fd = accept(sock_fd, (struct sockaddr *)&sock_client, (socklen_t *)&len);
memptr = push(memptr, new_sock_fd);
if (new_sock_fd == -1){
perror("accept");
exit(1);
}
pthread_create(&(thread[threadCount]), NULL, (void*)&serve, (void *)&new_sock_fd);
pthread_join(thread[threadCount++], NULL);
printf("threadCount = %d\n", threadCount);
sleep(1);
}
return 0;
}
void serve(void* fd){
int* new_sock_fd = (int*)fd;
Packet packet;
while(1){
bzero(&packet, sizeof(packet));
read(*new_sock_fd , &packet, sizeof(packet));
printf("%d\n", *new_sock_fd);
//printf("recipientId = %d\n", packet.recipientId);
// printf("message = %s\n", packet.data);
write(memptr[packet.recipientId - 1], packet.data, 1024);
}
pthread_exit(0);
}
and the tcp.h looks like
#ifndef __TCP_H__
# define __TCP_H__
typedef struct {
int recipientId; // this is the reciever ID
char data[1024]; // this is the main data part
}Packet;
#endif /* __TCP_H__ */
and each client.h looks like this
#include <stdio.h>
#include "./../linux.h"
#include "./tcp.h"
#include <pthread.h>
void print(void);
void scan(void);
int sock_fd;
int main(int argc, char** argv) {
if (argc != 3){
printf("\nUsage: ./client port_number server_ip\n");
return 1;
}
static struct sockaddr_in sock;
int len;
pthread_t thread1, thread2;
sock_fd = socket(PF_INET, SOCK_STREAM, 0);
if (sock_fd == -1){
perror("socket");
exit(1);
}
sock.sin_family = PF_INET;
sock.sin_port = htons(atoi(argv[1]));
sock.sin_addr.s_addr = inet_addr(argv[2]);
len = sizeof(sock);
if ( connect(sock_fd, (struct sockaddr *)&sock , len) == -1 ){
perror("connect");
exit(1);
}
pthread_create(&thread1, NULL, (void*)&print, NULL);
pthread_create(&thread2, NULL, (void*)&scan, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return 0;
}
void print(){
char messege[1024];
while(1){
if (read(sock_fd, messege, 1024) == -1){
perror("read");
return;
}
printf("messege = %s\n", messege);
}
pthread_exit(0);
}
void scan(void){
Packet packet;
while(1){
printf("Enter the reciver ID: ");
scanf("%d", &packet.recipientId);
printf("Enter the data: ");
scanf("%s", packet.data);
if ( write(sock_fd, &packet, sizeof(packet)) == -1) {
perror("read");
return;
}
}
pthread_exit(0);
}
Now the problems are
when I am running the server & the in 2 terminals, 2 clients after each client is accepted threadCount should be printed at the server end but it is not printing. It means the execution stops/skips after the first pthread_join but WHY ??
After connecting two threads, when I sent the data from 1st client to the 1st client itself, it works but not from the 1st client to the 2nd client..rather it is sending to the server terminal window. WHY ??
When sent from the second client nothing works ( sending itself or client 1)..WHY??
Please help..And thanks for patiently reading all the codes above.
TCP is a byte stream protocol, not a message protocol. You are calling TCP functions and expecting them to send or receive messages. They don't. If you want to send or receive messages, you have to implement a message protocol, write functions that send and receive messages, and then call those functions.
if (read(sock_fd, messege, 1024) == -1){
perror("read");
return;
}
printf("messege = %s\n", messege);
This call to printf is a disaster. The %s format specifier is for C-style strings, not arbitrary chunks of bytes received from a byte stream. For the most obvious way to see how bad this is, consider this --- how should printf decide how many bytes to print? You threw away the value read returned after you compared it to -1, so you have no idea how many bytes you received.
I'm sure there are other issues with your code, but the fact that the fundamental design is broken makes it not worth fixing those issues. Instead, design a message protocol and implement that.
I am trying to learn device drivers and I started with char device driver. I implemented a small program which is able to read/write from/to kernel buffer. Further, I tried to implement memory mapping and this is not working properly. When I am trying to read through a simple process by mapping my kernel module, it is giving me garbage value. Can any one help with this?
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h> //printk()
#include<linux/errno.h>
#include<linux/types.h>
#include<linux/proc_fs.h>
#include<asm/uaccess.h> //copy_from,to_user
#include<linux/mm.h> //remap_pfn_range
//#include<linux/mman.h> //private_mapping_ok
#define BUFF_SIZE 128
#define DEV_NAME "MyDevice"
MODULE_LICENSE("GPL");
//Method declarations
int mod_open(struct inode *,struct file *);
int mod_release(struct inode *,struct file *);
ssize_t mod_read(struct file *,char *,size_t ,loff_t *);
ssize_t mod_write(struct file *,char *,size_t ,loff_t *);
int mod_mmap(struct file *, struct vm_area_struct *);
void mod_exit(void);
int mod_init(void);
//Structure that declares the usual file access functions
struct file_operations mod_fops = {
read: mod_read,
write: mod_write,
open: mod_open,
release: mod_release,
mmap: mod_mmap
};
static const struct vm_operations_struct mod_mem_ops = {
};
module_init(mod_init);
module_exit(mod_exit);
char *read_buf;
char *write_buf;
static int Major;
//static int Device_Open = 0;
int buffsize = 0;
int mod_init(void)
{
Major = register_chrdev(0,DEV_NAME,&mod_fops);
if(Major < 0)
{
printk(KERN_ALERT "Can not register %s. No major number alloted",DEV_NAME);
return Major;
}
//allocate memory to buffers
read_buf = kmalloc(BUFF_SIZE, GFP_KERNEL);
write_buf = kmalloc(BUFF_SIZE, GFP_KERNEL);
if(!read_buf || !write_buf)
{
mod_exit();
return -ENOMEM;
}
//reset buffers
memset(read_buf,0, BUFF_SIZE);
memset(write_buf,0, BUFF_SIZE);
printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
printk(KERN_INFO "the driver, create a dev file with\n");
printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n",DEV_NAME, Major);
printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
printk(KERN_INFO "the device file.\n");
printk(KERN_INFO "Remove the device file and module when done.\n");
return 0;
}
void mod_exit(void)
{
unregister_chrdev(Major,"memory");
if(read_buf) kfree(read_buf);
if(write_buf) kfree(write_buf);
printk(KERN_INFO "removing module\n");
}
int mod_mmap(struct file *filp, struct vm_area_struct *vma)
{
size_t size = vma->vm_end - vma->vm_start;
vma->vm_ops = &mod_mem_ops;
/* Remap-pfn-range will mark the range VM_IO */
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot)) {
return -EAGAIN;
}
printk(KERN_INFO "VMA Open. Virt_addr: %lx, phy_addr: %lx\n",vma->vm_start, vma->vm_pgoff<<PAGE_SHIFT);
return 0;
}
ssize_t mod_read(struct file *filp, char *buf, size_t len, loff_t *f_pos)
{
ssize_t bytes;
if(buffsize < len)
bytes = buffsize;
else
bytes = len;
printk(KERN_INFO "Buffer size availabe: %d\n", buffsize);
printk(KERN_INFO "VMA Open. read buffer initial: %lx\n",read_buf);
if(bytes == 0)
return 0;
int retval = copy_to_user(buf,read_buf,bytes);
if(retval)
{
printk(KERN_INFO "copy_to_user fail");
return -EFAULT;
}
else
{
printk(KERN_INFO "copy_to_user succeeded\n");
buffsize -= bytes;
return bytes;
}
}
ssize_t mod_write( struct file *filp,char *buf, size_t len, loff_t *f_pos)
{
memset(read_buf,0,BUFF_SIZE);
memset(write_buf,0,BUFF_SIZE);
if(len > BUFF_SIZE)
{
printk(KERN_ALERT "Buffer not available. Writing only %d bytes.\n",BUFF_SIZE);
len = BUFF_SIZE;
}
printk(KERN_INFO "User space msg size: %d\n",len);
int retval = copy_from_user(read_buf,buf,len);
printk(KERN_INFO "read %d bytes as: %s\n", retval,read_buf);
// memcpy(write_buf,read_buf,len);
// printk(KERN_INFO "written: %s\n", write_buf);
buffsize = len;
return len;
}
int mod_open(struct inode *inode, struct file *filp){return 0;}
int mod_release(struct inode *inode, struct file *filp) {return 0;}
The program which is trying to access this device driver:
#include<stdio.h>
#include<sys/fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/mman.h>
int main(int argc,char *argv[])
{
int fd,n,len;
char *buff;
if(argc != 3)
{
printf("Too few arguments.\n");
exit(EXIT_FAILURE);
}
buff = (char *)malloc(128);
if(strcmp(argv[1],"read")==0)
{
if(-1 == (fd = open("/dev/MyDevice",O_RDONLY)))
{
printf("Device open fail. Error: %s",strerror(errno));
exit(EXIT_FAILURE);
}
memset(buff,0,128);
if(-1 == (buff = mmap(0,128,PROT_READ,MAP_SHARED | MAP_NORESERVE,fd,0)))
{
printf("Mapping failed. Error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* if(-1 == (n = read(fd,buff,128)))
{
printf("Device read fail. Error: %s",strerror(errno));
exit(EXIT_FAILURE);
}
*/
printf("Read from device:\n%s\n",buff);
close(fd);
}
else if(strcmp(argv[1],"write")==0)
{
len = strlen(argv[2]);
if(-1 == (fd = open("/dev/MyDevice",O_WRONLY)))
{
printf("Device open fail. Error: %s",strerror(errno));
exit(EXIT_FAILURE);
}
if(-1 == (n = write(fd,argv[2],len)))
{
printf("Device write fail. Error: %s",strerror(errno));
exit(EXIT_FAILURE);
}
printf("Written %d bytes successfully.\n",n);
close(fd);
}
else
{
printf("Invalid argument..\n");
exit(EXIT_FAILURE);
}
return 0;
}
I got the error in my code. I was not mapping my buffer to vma->vm_pgoff. Just add following code before calling rmap_pfn_range, then this code will work fine
vma->vm_pgoff = virt_to_phys(read_buff)>>PAGE_SHIFT;
There are still several potential issues in your code although you found the root cause.
"vma->vm_pgoff = virt_to_phys(read_buff)>>PAGE_SHIFT"
It is not very good practice to program in this example, as basically you are overwriting a user file offset (in PAGE size unit). If your driver need to support mmap a memory offset, then obvious there is a issue. In this case, you can just pass virt_to_phys(read_buff)>>PAGE_SHIFT in place.
It is not recommended to use kmalloc to allocate the memory for remap purpose, as it is required to be page aligned, you can just use the kernel page APIs, like get_free_page to allocate the memory, further more, it is better to remap the memory in units of PAGE size, rather than 128 bytes here.
I am trying to set a read timeout on a file descriptor representing a PTY. I have set VMIN = 0 and VTIME = 10 in termios, which I expect to return when a character is available, or after a second if no characters are available. However, my program sits forever in the read call.
Is there something special about PTY that makes this not work? Are there other TERMIOS settings that cause this to work? I tried this same configuration on the stdin file descriptor and it worked as expected.
#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#define debug(...) fprintf (stderr, __VA_ARGS__)
static void set_term (int fd)
{
struct termios termios;
int res;
res = tcgetattr (fd, &termios);
if (res) {
debug ("TERM get error\n");
return;
}
cfmakeraw (&termios);
termios.c_lflag &= ~(ICANON);
termios.c_cc[VMIN] = 0;
termios.c_cc[VTIME] = 10; /* One second */
res = tcsetattr (fd, TCSANOW, &termios);
if (res) {
debug ("TERM set error\n");
return;
}
}
int get_term (void)
{
int fd;
int res;
char *name;
fd = posix_openpt (O_RDWR);
if (fd < 0) {
debug ("Error opening PTY\n");
exit (1);
}
res = grantpt (fd);
if (res) {
debug ("Error granting PTY\n");
exit (1);
}
res = unlockpt (fd);
if (res) {
debug ("Error unlocking PTY\n");
exit (1);
}
name = ptsname (fd);
debug ("Attach terminal on %s\n", name);
return fd;
}
int main (int argc, char **argv)
{
int read_fd;
int bytes;
char c;
read_fd = get_term ();
set_term (read_fd);
bytes = read (read_fd, &c, 1);
debug ("Read returned\n");
return 0;
}
From the linux pty (7) manpage (italics are mine):
A pseudoterminal (sometimes abbreviated "pty") is a pair of virtual character devices that
provide a bidirectional communication channel. One end of the channel is called the
master; the other end is called the slave. The slave end of the pseudoterminal provides
an interface that behaves exactly like a classical terminal
Your program, however, is reading from the master, which cannot be expected to behave exactly like a terminal device
If you change/expand the last few lines of get_term thusly ...
int slave_fd = open (name, O_RDWR); /* now open the slave end..*/
if (slave_fd < 0) {
debug ("Error opening slave PTY\n");
exit (1);
}
return slave_fd; /* ... and use it instead of the master..*/
... your example program will work as expected.