Kernel module SMI

integration of TX stream
dma reader and writer corrections
using mb() for memory boundary
bug_fixes_integration_tx
David Michaeli 2023-05-30 18:27:29 +03:00
rodzic 0595990c8d
commit 35d4a04295
3 zmienionych plików z 62 dodań i 185 usunięć

Wyświetl plik

@ -1,166 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <pthread.h>
#include "../caribou_smi.h"
#include "old/bcm2835_smi.h"
#define SMI_STREAM_IOC_GET_NATIVE_BUF_SIZE _IO(BCM2835_SMI_IOC_MAGIC, 3)
#define SMI_STREAM_IOC_SET_NON_BLOCK_READ _IO(BCM2835_SMI_IOC_MAGIC, 4)
#define SMI_STREAM_IOC_SET_NON_BLOCK_WRITE _IO(BCM2835_SMI_IOC_MAGIC, 5)
#define SMI_STREAM_IOC_SET_STREAM_STATUS _IO(BCM2835_SMI_IOC_MAGIC, 6)
static void setup_settings (struct smi_settings *settings)
{
settings->read_setup_time = 0;
settings->read_strobe_time = 5;
settings->read_hold_time = 0;
settings->read_pace_time = 0;
settings->write_setup_time = 0;
settings->write_hold_time = 0;
settings->write_pace_time = 0;
settings->write_strobe_time = 4;
settings->data_width = SMI_WIDTH_8BIT;
settings->dma_enable = 1;
settings->pack_data = 1;
settings->dma_passthrough_enable = 1;
}
pthread_t tid;
int fd = -1;
size_t native_batch_length_bytes = 0;
int thread_running = 0;
void* read_thread(void *arg)
{
fd_set set;
int rv;
int timeout_num_millisec = 500;
uint8_t *buffer = malloc(native_batch_length_bytes);
int size_of_buf = native_batch_length_bytes;
while (thread_running)
{
while (1)
{
struct timeval timeout = {0};
FD_ZERO(&set); // clear the set mask
FD_SET(fd, &set); // add our file descriptor to the set - and only it
int num_sec = timeout_num_millisec / 1000;
timeout.tv_sec = num_sec;
timeout.tv_usec = (timeout_num_millisec - num_sec*1000) * 1000;
rv = select(fd + 1, &set, NULL, NULL, &timeout);
if(rv == -1)
{
int error = errno;
switch(error)
{
case EINTR: // A signal was caught.
continue;
case EBADF: // An invalid file descriptor was given in one of the sets.
// (Perhaps a file descriptor that was already closed, or one on which an error has occurred.)
case EINVAL: // nfds is negative or the value contained within timeout is invalid.
case ENOMEM: // unable to allocate memory for internal tables.
default: goto exit;
};
}
else if (rv == 0)
{
printf("Read poll timeout\n");
break;
}
else if (FD_ISSET(fd, &set))
{
int num_read = read(fd, buffer, size_of_buf);
printf("Read %d bytes\n", num_read);
break;
}
}
}
exit:
free(buffer);
return NULL;
}
int main()
{
char smi_file[] = "/dev/smi";
struct smi_settings settings = {0};
fd = open(smi_file, O_RDWR);
if (fd < 0)
{
printf("can't open smi driver file '%s'\n", smi_file);
return -1;
}
// Get the current settings
int ret = ioctl(fd, BCM2835_SMI_IOC_GET_SETTINGS, &settings);
if (ret != 0)
{
printf("failed reading ioctl from smi fd (settings)\n");
close (fd);
return -1;
}
// apply the new settings
setup_settings(&settings);
ret = ioctl(fd, BCM2835_SMI_IOC_WRITE_SETTINGS, &settings);
if (ret != 0)
{
printf("failed writing ioctl to the smi fd (settings)\n");
close (fd);
return -1;
}
// set the address to idle
ret = ioctl(fd, BCM2835_SMI_IOC_ADDRESS, caribou_smi_address_idle);
if (ret != 0)
{
printf("failed setting smi address (idle / %d) to device\n", caribou_smi_address_idle);
close (fd);
return -1;
}
// get the native batch length in bytes
ret = ioctl(fd, SMI_STREAM_IOC_GET_NATIVE_BUF_SIZE, &native_batch_length_bytes);
if (ret != 0)
{
printf("failed reading native batch length, setting default\n");
native_batch_length_bytes = (1024)*(1024)/2;
}
printf("Native batch size: %u\n", native_batch_length_bytes);
// start streaming data
ret = ioctl(fd, SMI_STREAM_IOC_SET_STREAM_STATUS, 1);
// start the reader thread
thread_running = 1;
int err = pthread_create(&tid, NULL, &read_thread, NULL);
if (err != 0)
{
printf("\ncan't create thread :[%s]", strerror(err));
}
getchar();
thread_running = 0;
pthread_join(tid, NULL);
ret = ioctl(fd, SMI_STREAM_IOC_SET_STREAM_STATUS, 0);
close (fd);
return 0;
}

Wyświetl plik

@ -176,7 +176,7 @@ static void set_state(smi_stream_state_en state)
{
inst->cur_address = (smi_stream_dir_device_to_smi<<ADDR_DIR_OFFSET) | (smi_stream_channel_1<<ADDR_CH_OFFSET);
}
else if (state == smi_stream_tx)
else if (state == smi_stream_tx_channel)
{
inst->cur_address = smi_stream_dir_smi_to_device<<ADDR_DIR_OFFSET;
}
@ -250,17 +250,21 @@ static inline int smi_enabled(struct bcm2835_smi_instance *inst)
}*/
/***************************************************************************/
static void smi_init_programmed_read(struct bcm2835_smi_instance *smi_inst, int num_transfers)
static int smi_init_programmed_read(struct bcm2835_smi_instance *smi_inst, int num_transfers)
{
int smics_temp;
int success = 0;
/* Disable the peripheral: */
smics_temp = read_smi_reg(smi_inst, SMICS) & ~(SMICS_ENABLE | SMICS_WRITE);
write_smi_reg(smi_inst, smics_temp, SMICS);
// wait for the ENABLE to go low
while (read_smi_reg(smi_inst, SMICS) & SMICS_ENABLE)
;
BUSY_WAIT_WHILE_TIMEOUT(smi_enabled(smi_inst), 100000U, success);
if (!success)
{
return -1;
}
/* Program the transfer count: */
write_smi_reg(smi_inst, num_transfers, SMIL);
@ -274,31 +278,38 @@ static void smi_init_programmed_read(struct bcm2835_smi_instance *smi_inst, int
/* IO barrier - to be sure that the last request have
been dispatched in the correct order
*/
smp_mb();
mb();
// busy wait as long as the transaction is active (taking place)
while (read_smi_reg(smi_inst, SMICS) & SMICS_ACTIVE)
;
BUSY_WAIT_WHILE_TIMEOUT(smi_is_active(smi_inst), 100000U, success);
if (!success)
{
return -2;
}
// Clear the FIFO (reset it to zero contents)
write_smi_reg(smi_inst, smics_temp, SMICS);
// Start the transaction
smics_temp |= SMICS_START;
write_smi_reg(smi_inst, smics_temp, SMICS);
return 0;
}
/***************************************************************************/
static void smi_init_programmed_write(struct bcm2835_smi_instance *smi_inst, int num_transfers)
static int smi_init_programmed_write(struct bcm2835_smi_instance *smi_inst, int num_transfers)
{
int smics_temp;
int success = 0;
/* Disable the peripheral: */
smics_temp = read_smi_reg(smi_inst, SMICS) & ~SMICS_ENABLE;
write_smi_reg(smi_inst, smics_temp, SMICS);
// Wait as long as the SMI is still enabled
while (read_smi_reg(smi_inst, SMICS) & SMICS_ENABLE)
;
BUSY_WAIT_WHILE_TIMEOUT(smi_enabled(smi_inst), 100000U, success);
if (!success)
{
return -1;
}
/* Program the transfer count: */
write_smi_reg(smi_inst, num_transfers, SMIL);
@ -309,6 +320,7 @@ static void smi_init_programmed_write(struct bcm2835_smi_instance *smi_inst, int
smics_temp |= SMICS_START;
write_smi_reg(smi_inst, smics_temp, SMICS);
return 0;
}
@ -488,11 +500,23 @@ ssize_t stream_smi_user_dma( struct bcm2835_smi_instance *inst,
// we have only 8 bit width
if (dma_dir == DMA_DEV_TO_MEM)
{
smi_init_programmed_read(inst, DMA_BOUNCE_BUFFER_SIZE);
int ret = smi_init_programmed_read(inst, DMA_BOUNCE_BUFFER_SIZE);
if (ret != 0)
{
spin_unlock(&inst->transaction_lock);
dev_err(inst->dev, "smi_init_programmed_read returned %d", ret);
return 0;
}
}
else
{
smi_init_programmed_write(inst, DMA_BOUNCE_BUFFER_SIZE);
int ret = smi_init_programmed_write(inst, DMA_BOUNCE_BUFFER_SIZE);
if (ret != 0)
{
spin_unlock(&inst->transaction_lock);
dev_err(inst->dev, "smi_init_programmed_write returned %d", ret);
return 0;
}
}
//printk(KERN_ERR DRIVER_NAME": SPIN-UNLOCK\n");
@ -517,6 +541,10 @@ int reader_thread_stream_function(void *pv)
// check if the streaming state is on, if not, sleep and check again
if (inst->state != smi_stream_rx_channel_0 && inst->state != smi_stream_rx_channel_1)
{
//mutex_lock(&inst->read_lock);
//kfifo_reset(&inst->rx_fifo);
//mutex_unlock(&inst->read_lock);
msleep(5);
continue;
}
@ -531,8 +559,10 @@ int reader_thread_stream_function(void *pv)
count = stream_smi_user_dma(inst->smi_inst, DMA_DEV_TO_MEM, &bounce, current_dma_buffer);
if (count != DMA_BOUNCE_BUFFER_SIZE || bounce == NULL)
{
dev_err(inst->dev, "reader_thread return illegal count = %d", count);
//current_dma_buffer = 1-current_dma_buffer;
dev_err(inst->dev, "stream_smi_user_dma returned illegal count = %d", count);
spin_lock(&inst->smi_inst->transaction_lock);
dmaengine_terminate_sync(inst->smi_inst->dma_chan);
spin_unlock(&inst->smi_inst->transaction_lock);
continue;
}
@ -570,7 +600,10 @@ int reader_thread_stream_function(void *pv)
// try to wait more, unless someone tells us to stop
if (down_timeout(&bounce->callback_sem, msecs_to_jiffies(1000)))
{
dev_info(inst->dev, "DMA bounce timed out");
dev_info(inst->dev, "Reader DMA bounce timed out");
spin_lock(&inst->smi_inst->transaction_lock);
dmaengine_terminate_sync(inst->smi_inst->dma_chan);
spin_unlock(&inst->smi_inst->transaction_lock);
}
else
{
@ -612,7 +645,7 @@ int writer_thread_stream_function(void *pv)
while(!kthread_should_stop())
{
// check if the streaming state is on, if not, sleep and check again
if (inst->state != smi_stream_tx)
if (inst->state != smi_stream_tx_channel)
{
msleep(5);
continue;
@ -654,15 +687,25 @@ int writer_thread_stream_function(void *pv)
if (count != DMA_BOUNCE_BUFFER_SIZE)
{
// error
dev_err(inst->dev, "stream_smi_user_dma error");
continue;
}
// Wait for current chunk to complete
if (down_timeout(&bounce->callback_sem, msecs_to_jiffies(1000)))
{
dev_err(inst->dev, "DMA bounce timed out");
dev_err(inst->dev, "Writer DMA bounce timed out");
spin_lock(&inst->smi_inst->transaction_lock);
dmaengine_terminate_sync(inst->smi_inst->dma_chan);
spin_unlock(&inst->smi_inst->transaction_lock);
}
}
else
{
// if doen't have enough data, invoke poll
inst->writeable = true;
wake_up_interruptible(&inst->poll_event);
}
}
dev_info(inst->dev, "Left writer thread");

Wyświetl plik

@ -69,7 +69,7 @@ typedef enum
smi_stream_idle = 0,
smi_stream_rx_channel_0 = 1,
smi_stream_rx_channel_1 = 2,
smi_stream_tx = 3,
smi_stream_tx_channel = 3,
} smi_stream_state_en;
#ifdef __KERNEL__