Problem play audio file on STM32F7 with SAI - audio

The task of this project is to read a .wav file from an external flash drive and play it, and you can listen with normal headphones.
We use an STM32 (STM32F769IITx) microcontroller with a WM8985, for the transmission we use an SAI with the DMA.
This is how we set the microcontroller:
SAI setting:
enter image description here
DMA setting:
enter image description here
Clock setting:
enter image description here
Here's the code:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
#include "usb_host.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ff.h"
#include "stdbool.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define AUDIO_BUFFER_SIZE 1024
#define WAV_FILE1 "1.wav"
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
SAI_HandleTypeDef hsai_BlockB1;
SAI_HandleTypeDef hsai_BlockA1;
DMA_HandleTypeDef hdma_sai1_a;
/* USER CODE BEGIN PV */
extern ApplicationTypeDef Appli_state;
//Detect flash unit
int isSdCardMounted=0;
//WAV File System variables
static FIL wavFile;
//WAV Audio Buffer
static uint32_t fileLength;
static uint32_t audioBuffer[AUDIO_BUFFER_SIZE];
static __IO uint32_t audioRemainSize = 0;
//WAV Player
static uint32_t samplingFreq;
static UINT playerReadBytes = 0;
static bool isFinished=0;
//WAV Player process states
typedef enum
{
PLAYER_CONTROL_Idle=0,
PLAYER_CONTROL_HalfBuffer,
PLAYER_CONTROL_FullBuffer,
PLAYER_CONTROL_EndOfFile,
}PLAYER_CONTROL_e;
static volatile PLAYER_CONTROL_e playerControlSM = PLAYER_CONTROL_Idle;
typedef struct
{
uint32_t ChunkID; /* 0 */
uint32_t FileSize; /* 4 */
uint32_t FileFormat; /* 8 */
uint32_t SubChunk1ID; /* 12 */
uint32_t SubChunk1Size; /* 16*/
uint16_t AudioFormat; /* 20 */
uint16_t NbrChannels; /* 22 */
uint32_t SampleRate; /* 24 */
uint32_t ByteRate; /* 28 */
uint16_t BlockAlign; /* 32 */
uint16_t BitPerSample; /* 34 */
uint32_t SubChunk2ID; /* 36 */
uint32_t SubChunk2Size; /* 40 */
}WAV_HeaderTypeDef;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2C1_Init(void);
static void MX_SAI1_Init(void);
void MX_USB_HOST_Process(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void InitializeWM8985()
{
uint8_t data[2];
//Reset
data[0] = 0x00;
data[1] = 0x00;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Set: BIASCUT bit
data[0] = 0x7B;
data[1] = 0x00;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Mute: LOUT1 Set: Volume update
data[0] = 0x69;
data[1] = 0x40;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Mute: ROUT1 Set: Volume update
data[0] = 0x6B;
data[1] = 0x40;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Mute: LOUT2 Set: Volume update
data[0] = 0x6D;
data[1] = 0x40;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Mute: ROUT2 Set: Volume update
data[0] = 0x6F;
data[1] = 0x40;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Mute: OUT3
data[0] = 0x70;
data[1] = 0x41;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Mute: OUT4
data[0] = 0x72;
data[1] = 0x41;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: L/ROUT1 amplifier core
data[0] = 0x05;
data[1] = 0x80;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: L/ROUT2 amplifier core
data[0] = 0x06;
data[1] = 0x60;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: POBCTRL
data[0] = 0x54;
data[1] = 0x04;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: DACs; Mixers
data[0] = 0x06;
data[1] = 0x6F;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: VMIDSEL = 75kohm; BIASEN; BUFIOEN
data[0] = 0x02;
data[1] = 0x2D;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: I2S; 24-bit
data[0] = 0x08;
data[1] = 0x10;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Disable: CLKSEL Enable: MCLKDIV = n/1; BCLKDIV = n/4
data[0] = 0x0C;
data[1] = 0x40;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: DACVU; Set: DACLVOL=0dB;
data[0] = 0x17;
data[1] = 0xFF;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Enable: DACVU; Set: DACRVOL=0dB;
data[0] = 0x19;
data[1] = 0xFF;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Disable: LIN; LIP; RIP; RIN
data[0] = 0x58;
data[1] = 0x00;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
HAL_Delay(100); //Delay to allow VMID to rise sufficiently before enabling outputs
//Un-mute: LOUT1 Set: LOUT1VOL = 0dB
data[0] = 0x69;
data[1] = 0x11;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Un-mute: ROUT1 Set: ROUT1VOL = 0dB
data[0] = 0x6B;
data[1] = 0x11;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Un-mute: LOUT2 Set: LOUT2VOL = 0dB
data[0] = 0x6D;
data[1] = 0x11;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Un-mute: ROUT2 Set: ROUT1VOL = 0dB
data[0] = 0x6F;
data[1] = 0x11;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Un-mute: OUT3
data[0] = 0x70;
data[1] = 0x41;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Un-mute: OUT4
data[0] = 0x72;
data[1] = 0x41;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
//Disable: POBCTRL
data[0] = 0x54;
data[1] = 0x00;
HAL_I2C_Master_Transmit(&hi2c1, 0x34, data, 2, HAL_MAX_DELAY);
}
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
playerControlSM = PLAYER_CONTROL_FullBuffer;
}
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
playerControlSM = PLAYER_CONTROL_HalfBuffer;
}
/* USER CODE END 0 */
/**
* #brief The application entry point.
* #retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_QUADSPI_Init();
MX_SAI1_Init();
MX_USB_HOST_Init();
MX_FATFS_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(USB_EN_POWER_GPIO_Port, USB_EN_POWER_Pin, GPIO_PIN_SET); //Enable the USB port on the board
HAL_Delay(1000);
InitializeWM8985();
HAL_StatusTypeDef SAI_status = HAL_SAI_Transmit_DMA (&hsai_BlockA1, (uint32_t *) audioBuffer, AUDIO_BUFFER_SIZE);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
MX_USB_HOST_Process();
/* USER CODE BEGIN 3 */
if(Appli_state == APPLICATION_DISCONNECT)
{
isSdCardMounted = 0;
}
if(Appli_state == APPLICATION_READY && SAI_status == HAL_OK)
{
if(!isSdCardMounted)
{
f_mount(&USBHFatFS, (const TCHAR*)USBHPath, 0);
isSdCardMounted = 1;
}
HAL_Delay(500);
WAV_HeaderTypeDef wavHeader;
UINT readBytes = 0;
if(f_open(&wavFile, WAV_FILE1, FA_READ) != FR_OK)
{
return;
}
f_read(&wavFile, &wavHeader, sizeof(wavHeader), &readBytes);
fileLength = wavHeader.FileSize;
samplingFreq = wavHeader.SampleRate;
isFinished = false;
f_lseek(&wavFile, 0);
f_read (&wavFile, &audioBuffer[0], AUDIO_BUFFER_SIZE, &playerReadBytes);
audioRemainSize = fileLength - playerReadBytes;
while(1)
{
switch(playerControlSM)
{
case PLAYER_CONTROL_Idle:
break;
case PLAYER_CONTROL_HalfBuffer:
playerReadBytes = 0;
playerControlSM = PLAYER_CONTROL_Idle;
f_read (&wavFile, &audioBuffer[0], AUDIO_BUFFER_SIZE/2, &playerReadBytes);
if(audioRemainSize > (AUDIO_BUFFER_SIZE / 2))
{
audioRemainSize -= playerReadBytes;
}
else
{
audioRemainSize = 0;
playerControlSM = PLAYER_CONTROL_EndOfFile;
}
break;
case PLAYER_CONTROL_FullBuffer:
playerReadBytes = 0;
playerControlSM = PLAYER_CONTROL_Idle;
f_read (&wavFile, &audioBuffer[AUDIO_BUFFER_SIZE/2], AUDIO_BUFFER_SIZE/2, &playerReadBytes);
if(audioRemainSize > (AUDIO_BUFFER_SIZE / 2))
{
audioRemainSize -= playerReadBytes;
}
else
{
audioRemainSize = 0;
playerControlSM = PLAYER_CONTROL_EndOfFile;
}
break;
case PLAYER_CONTROL_EndOfFile:
f_close(&wavFile);
isFinished = true;
playerControlSM = PLAYER_CONTROL_Idle;
break;
}
}
}
}
/* USER CODE END 3 */
}
/**
* #brief System Clock Configuration
* #retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 240;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 5;
RCC_OscInitStruct.PLL.PLLR = 2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief Peripherals Common Clock Configuration
* #retval None
*/
void PeriphCommonClock_Config(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1|RCC_PERIPHCLK_CLK48;
PeriphClkInitStruct.PLLSAI.PLLSAIN = 330;
PeriphClkInitStruct.PLLSAI.PLLSAIR = 2;
PeriphClkInitStruct.PLLSAI.PLLSAIQ = 5;
PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV2;
PeriphClkInitStruct.PLLSAIDivQ = 6;
PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_2;
PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief I2C1 Initialization Function
* #param None
* #retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x007074AF;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}
/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
/**
* #brief SAI1 Initialization Function
* #param None
* #retval None
*/
static void MX_SAI1_Init(void)
{
/* USER CODE BEGIN SAI1_Init 0 */
/* USER CODE END SAI1_Init 0 */
/* USER CODE BEGIN SAI1_Init 1 */
/* USER CODE END SAI1_Init 1 */
hsai_BlockA1.Instance = SAI1_Block_A;
hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;
hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;
hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_32BIT, 2) != HAL_OK)
{
Error_Handler();
}
hsai_BlockB1.Instance = SAI1_Block_B;
hsai_BlockB1.Init.Protocol = SAI_FREE_PROTOCOL;
hsai_BlockB1.Init.AudioMode = SAI_MODESLAVE_RX;
hsai_BlockB1.Init.DataSize = SAI_DATASIZE_8;
hsai_BlockB1.Init.FirstBit = SAI_FIRSTBIT_MSB;
hsai_BlockB1.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE;
hsai_BlockB1.Init.Synchro = SAI_SYNCHRONOUS;
hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
hsai_BlockB1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
hsai_BlockB1.FrameInit.FrameLength = 8;
hsai_BlockB1.FrameInit.ActiveFrameLength = 1;
hsai_BlockB1.FrameInit.FSDefinition = SAI_FS_STARTFRAME;
hsai_BlockB1.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW;
hsai_BlockB1.FrameInit.FSOffset = SAI_FS_FIRSTBIT;
hsai_BlockB1.SlotInit.FirstBitOffset = 0;
hsai_BlockB1.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE;
hsai_BlockB1.SlotInit.SlotNumber = 1;
hsai_BlockB1.SlotInit.SlotActive = 0x00000000;
if (HAL_SAI_Init(&hsai_BlockB1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SAI1_Init 2 */
/* USER CODE END SAI1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA2_Stream1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn);
}
/**
* #brief GPIO Initialization Function
* #param None
* #retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOE, FLASH_WP_Pin|CODEC_AUDIO_CS_Pin|DAC_EXTERNAL_D0_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(ESAFORESI_RELAY_EN_GPIO_Port, ESAFORESI_RELAY_EN_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOF, IMPEDENZIMETRO_TEST_Pin|FLASH_HOLD_Pin|SOLENOIDE_1_MASSA_Pin|SOLENOIDE_2_MASSA_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_1_GREEN_GPIO_Port, LED_1_GREEN_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOG, SPARE_OUT_1_Pin|VBS_MATERASSO_O_CUSINO_Pin|VBS_POLARITY_Pin|ESAFORESI_SWITCHING_EN_Pin
|DAC_EXTERNAL_RW_Pin|AMPLI_AUDIO_EN_Pin|DAC_EXTERNAL_D5_Pin|DAC_EXTERNAL_D4_Pin
|DAC_EXTERNAL_D3_Pin|DAC_EXTERNAL_D2_Pin|DAC_EXTERNAL_D1_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOE, LED_4_RED_Pin|LED_3_GREEN_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, SPARE_OUT_2_Pin|VBS_LED_2_Pin|VBS_LED_1_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_2_RED_GPIO_Port, LED_2_RED_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOD, USB_EN_POWER_Pin|VBS_DAC_CS_Pin|SRAM_HOLD_SPI_Pin|SRAM_SPI_CS_Pin
|ESAFORESI_DAC_CS_Pin|DAC_EXTERNAL_D7_Pin|DAC_EXTERNAL_D6_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : FLASH_WP_Pin LED_4_RED_Pin LED_3_GREEN_Pin CODEC_AUDIO_CS_Pin
DAC_EXTERNAL_D0_Pin */
GPIO_InitStruct.Pin = FLASH_WP_Pin|LED_4_RED_Pin|LED_3_GREEN_Pin|CODEC_AUDIO_CS_Pin
|DAC_EXTERNAL_D0_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pins : ESAFORESI_RELAY_EN_Pin LED_1_GREEN_Pin */
GPIO_InitStruct.Pin = ESAFORESI_RELAY_EN_Pin|LED_1_GREEN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : ESAFORESI_RELAY_TEST_Pin SPARE_IN_1_Pin SPARE_IN_2_Pin */
GPIO_InitStruct.Pin = ESAFORESI_RELAY_TEST_Pin|SPARE_IN_1_Pin|SPARE_IN_2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/*Configure GPIO pins : IMPEDENZIMETRO_TEST_Pin FLASH_HOLD_Pin SOLENOIDE_1_MASSA_Pin SOLENOIDE_2_MASSA_Pin */
GPIO_InitStruct.Pin = IMPEDENZIMETRO_TEST_Pin|FLASH_HOLD_Pin|SOLENOIDE_1_MASSA_Pin|SOLENOIDE_2_MASSA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/*Configure GPIO pins : SPARE_OUT_1_Pin VBS_MATERASSO_O_CUSINO_Pin VBS_POLARITY_Pin ESAFORESI_SWITCHING_EN_Pin
DAC_EXTERNAL_RW_Pin AMPLI_AUDIO_EN_Pin DAC_EXTERNAL_D5_Pin DAC_EXTERNAL_D4_Pin
DAC_EXTERNAL_D3_Pin DAC_EXTERNAL_D2_Pin DAC_EXTERNAL_D1_Pin */
GPIO_InitStruct.Pin = SPARE_OUT_1_Pin|VBS_MATERASSO_O_CUSINO_Pin|VBS_POLARITY_Pin|ESAFORESI_SWITCHING_EN_Pin
|DAC_EXTERNAL_RW_Pin|AMPLI_AUDIO_EN_Pin|DAC_EXTERNAL_D5_Pin|DAC_EXTERNAL_D4_Pin
|DAC_EXTERNAL_D3_Pin|DAC_EXTERNAL_D2_Pin|DAC_EXTERNAL_D1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/*Configure GPIO pins : DIP_1_Pin DIP_2_Pin */
GPIO_InitStruct.Pin = DIP_1_Pin|DIP_2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pins : SPARE_OUT_2_Pin LED_2_RED_Pin VBS_LED_2_Pin VBS_LED_1_Pin */
GPIO_InitStruct.Pin = SPARE_OUT_2_Pin|LED_2_RED_Pin|VBS_LED_2_Pin|VBS_LED_1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : USB_FAULT_Pin */
GPIO_InitStruct.Pin = USB_FAULT_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(USB_FAULT_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : USB_EN_POWER_Pin VBS_DAC_CS_Pin SRAM_HOLD_SPI_Pin SRAM_SPI_CS_Pin
ESAFORESI_DAC_CS_Pin DAC_EXTERNAL_D7_Pin DAC_EXTERNAL_D6_Pin */
GPIO_InitStruct.Pin = USB_EN_POWER_Pin|VBS_DAC_CS_Pin|SRAM_HOLD_SPI_Pin|SRAM_SPI_CS_Pin
|ESAFORESI_DAC_CS_Pin|DAC_EXTERNAL_D7_Pin|DAC_EXTERNAL_D6_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/*Configure GPIO pins : DISPLAY_SPARE_3_Pin DISPLAY_SPARE_2_Pin DISPLAY_SPARE_1_Pin */
GPIO_InitStruct.Pin = DISPLAY_SPARE_3_Pin|DISPLAY_SPARE_2_Pin|DISPLAY_SPARE_1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* #brief This function is executed in case of error occurrence.
* #retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* #brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* #param file: pointer to the source file name
* #param line: assert_param error line source number
* #retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
The problem we have is the audio we listen to output is correct and the rhythm is similar to the original, but we have a lot of noise and it sounds metallic.
And we don't know how to solve or analyze the problem, suggestions are welcome to carry out further analysis and provide you with as much information to understand the problem.
We try to change board, but the problem is the same in the new one, change the SAI configuration.

Related

Segmentation Fault Error When Reading Two Serial Port with BeagleBone Black

I am getting Segmentation fault error while reading two diffentent serial communication line with using Debian GNU/Linux 7.4 on Beaglebone Black. One of them is CAN-BUS data. I am using Waveshares RS485/CAN CAPE module for this with using can-utils package. "https://github.com/linux-can/can-utils/blob/master/candump.c"
CAN log file
And the other one is UART data by a GPS module called uBlox GY-NEO6MV2 module. For the GPS I have this code which works perfectly;
#include <stdio.h>
#include <fcntl.h> /* File Control Definitions */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h> /* UNIX Standard Definitions */
#include <errno.h> /* ERROR Number Definitions */
#include <string.h> /* Array to String */
void main(void){
int fd;/*File Descriptor*/
/*------------------------------- Opening the Serial Port -------------------------------*/
/* Change /dev/ttyUSB0 to the one corresponding to your system */
while(1){
fd = open("/dev/ttyO2",O_RDWR | O_NOCTTY); /* ttyUSB0 is the FT232 based USB2SERIAL Converter */
/* O_RDWR - Read/Write access to serial port */
/* O_NOCTTY - No terminal will control the process */
/* Open in blocking mode,read will wait */
if(fd == -1) /* Error Checking */
printf("\n Error! in Opening ttyO2 ");
else
printf("\n ttyO2 Opened Successfully ");
/*---------- Setting the Attributes of the serial port using termios structure --------- */
struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
/* Setting the Baud rate */
cfsetispeed(&SerialPortSettings,B9600); /* Set Read Speed as 9600 */
cfsetospeed(&SerialPortSettings,B9600); /* Set Write Speed as 9600 */
/* 8N1 Mode */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 42; /* Read at least 51 characters */
SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly */
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
else
printf("\n BaudRate = 9600 \n StopBits = 1 \n Parity = none \n\n");
/*------------------------------- Read data from serial port -----------------------------*/
tcflush(fd, TCIFLUSH); /* Discards old data in the rx buffer */
char read_buffer[42]; /* Buffer to store the data received */
int bytes_read = 0; /* Number of bytes read by the read() system call */
int ia = 0; int a;
int test = 0;
char new_read[38];
char curr_read[33];
a = 0;
do{
bytes_read = read(fd,&read_buffer,42); /* Read the data */
if(read_buffer[0] == '$')
if(read_buffer[1] == 'G')
if(read_buffer[2] == 'P')
if(read_buffer[3] == 'G')
if(read_buffer[4] == 'G'){
for(ia=7;ia<bytes_read;ia++){ /*printing only the received characters*/
new_read[a] = read_buffer[ia];
printf("%c",read_buffer[ia]);
a = a+1;
test = 1;
}
strcpy(curr_read, new_read);
printf("\n%s \n", curr_read);
}
else
test = 0;
else
test = 0;
else
test = 0;
else
test = 0;
else
test = 0;
}while(test == 0);
close(fd); /* Close the serial port */
}
}
And for the CAN logging I am using the code in the link above. What I try to achive is logging two data in to same log file. I modified the code above a little to get the datas only that I need; which is timestamp and location coordinates.
GPS edited data
GPS module gives data every second so I am triyng to get one data from GPS and attach it to the next 1000 CAN data then write in to a .log file then read a new value from GPS. GPS modules communication bitrate is 9600kbps and CAN bitrate is 125000 kbps. GPS is connected to UART2 pin, CAN to UART1. When I try to combine two code into one I get the Segmentation fault error. I made a little research its UNIX error code while violeting the restiricted memory space. But these two codes works perfectly when working seperatly. This is where I got stucked.
The code I tried to merge is like;
/* for hardware timestamps - since Linux 2.6.30 */
#ifndef SO_TIMESTAMPING
#define SO_TIMESTAMPING 37
#endif
/* from #include <linux/net_tstamp.h> - since Linux 2.6.30 */
#define SOF_TIMESTAMPING_SOFTWARE (1<<4)
#define SOF_TIMESTAMPING_RX_SOFTWARE (1<<3)
#define SOF_TIMESTAMPING_RAW_HARDWARE (1<<6)
#define MAXSOCK 16 /* max. number of CAN interfaces given on the cmdline */
#define MAXIFNAMES 30 /* size of receive name index to omit ioctls */
#define MAXCOL 6 /* number of different colors for colorized output */
#define ANYDEV "any" /* name of interface to receive from any CAN interface */
#define ANL "\r\n" /* newline in ASC mode */
#define SILENT_INI 42 /* detect user setting on commandline */
#define SILENT_OFF 0 /* no silent mode */
#define SILENT_ANI 1 /* silent mode with animation */
#define SILENT_ON 2 /* silent mode (completely silent) */
static char *cmdlinename[MAXSOCK];
static __u32 dropcnt[MAXSOCK];
static __u32 last_dropcnt[MAXSOCK];
static char devname[MAXIFNAMES][IFNAMSIZ+1];
static int dindex[MAXIFNAMES];
static int max_devname_len; /* to prevent frazzled device name output */
const int canfd_on = 1;
#define MAXANI 4
const char anichar[MAXANI] = {'|', '/', '-', '\\'};
const char extra_m_info[4][4] = {"- -", "B -", "- E", "B E"};
extern int optind, opterr, optopt;
static volatile int running = 1;
void sigterm(int signo)
{
running = 0;
}
int idx2dindex(int ifidx, int socket) {
int i;
struct ifreq ifr;
for (i=0; i < MAXIFNAMES; i++) {
if (dindex[i] == ifidx)
return i;
}
/* create new interface index cache entry */
/* remove index cache zombies first */
for (i=0; i < MAXIFNAMES; i++) {
if (dindex[i]) {
ifr.ifr_ifindex = dindex[i];
if (ioctl(socket, SIOCGIFNAME, &ifr) < 0)
dindex[i] = 0;
}
}
for (i=0; i < MAXIFNAMES; i++)
if (!dindex[i]) /* free entry */
break;
if (i == MAXIFNAMES) {
fprintf(stderr, "Interface index cache only supports %d interfaces.\n",
MAXIFNAMES);
exit(1);
}
dindex[i] = ifidx;
ifr.ifr_ifindex = ifidx;
if (ioctl(socket, SIOCGIFNAME, &ifr) < 0)
perror("SIOCGIFNAME");
if (max_devname_len < strlen(ifr.ifr_name))
max_devname_len = strlen(ifr.ifr_name);
strcpy(devname[i], ifr.ifr_name);
#ifdef DEBUG
printf("new index %d (%s)\n", i, devname[i]);
#endif
return i;
}
int main(int argc, char **argv)
{
fd_set rdfs;
int s[MAXSOCK];
int bridge = 0;
useconds_t bridge_delay = 0;
unsigned char timestamp = 0;
unsigned char hwtimestamp = 0;
unsigned char down_causes_exit = 1;
unsigned char dropmonitor = 0;
unsigned char extra_msg_info = 0;
unsigned char silent = SILENT_INI;
unsigned char silentani = 0;
unsigned char color = 0;
unsigned char view = 0;
unsigned char log = 0;
unsigned char logfrmt = 0;
int count = 0;
int rcvbuf_size = 0;
int opt, ret;
int currmax, numfilter;
int join_filter;
char *ptr, *nptr;
struct sockaddr_can addr;
char ctrlmsg[CMSG_SPACE(sizeof(struct timeval) + 3*sizeof(struct timespec) + sizeof(__u32))];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
struct can_filter *rfilter;
can_err_mask_t err_mask;
struct canfd_frame frame;
int nbytes, i, maxdlen;
struct ifreq ifr;
struct timeval tv, last_tv;
struct timeval timeout, timeout_config = { 0, 0 }, *timeout_current = NULL;
FILE *logfile = NULL;
int fd;/*File Descriptor*/
struct termios SerialPortSettings; /* Create the structure */
signal(SIGTERM, sigterm);
signal(SIGHUP, sigterm);
signal(SIGINT, sigterm);
last_tv.tv_sec = 0;
last_tv.tv_usec = 0;
if (optind == argc) {
print_usage(basename(argv[0]));
exit(0);
}
if (logfrmt && view) {
fprintf(stderr, "Log file format selected: Please disable ASCII/BINARY/SWAP options!\n");
exit(0);
}
if (silent == SILENT_INI) {
if (log) {
fprintf(stderr, "Disabled standard output while logging.\n");
silent = SILENT_ON; /* disable output on stdout */
} else
silent = SILENT_OFF; /* default output */
}
currmax = argc - optind; /* find real number of CAN devices */
if (currmax > MAXSOCK) {
fprintf(stderr, "More than %d CAN devices given on commandline!\n", MAXSOCK);
return 1;
}
for (i=0; i < currmax; i++) {
ptr = argv[optind+i];
nptr = strchr(ptr, ',');
#ifdef DEBUG
printf("open %d '%s'.\n", i, ptr);
#endif
s[i] = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (s[i] < 0) {
perror("socket");
return 1;
}
cmdlinename[i] = ptr; /* save pointer to cmdline name of this socket */
if (nptr)
nbytes = nptr - ptr; /* interface name is up the first ',' */
else
nbytes = strlen(ptr); /* no ',' found => no filter definitions */
if (nbytes >= IFNAMSIZ) {
fprintf(stderr, "name of CAN device '%s' is too long!\n", ptr);
return 1;
}
if (nbytes > max_devname_len)
max_devname_len = nbytes; /* for nice printing */
addr.can_family = AF_CAN;
memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name));
strncpy(ifr.ifr_name, ptr, nbytes);
#ifdef DEBUG
printf("using interface name '%s'.\n", ifr.ifr_name);
#endif
if (strcmp(ANYDEV, ifr.ifr_name)) {
if (ioctl(s[i], SIOCGIFINDEX, &ifr) < 0) {
perror("SIOCGIFINDEX");
exit(1);
}
addr.can_ifindex = ifr.ifr_ifindex;
} else
addr.can_ifindex = 0; /* any can interface */
if (nptr) {
/* found a ',' after the interface name => check for filters */
/* determine number of filters to alloc the filter space */
numfilter = 0;
ptr = nptr;
while (ptr) {
numfilter++;
ptr++; /* hop behind the ',' */
ptr = strchr(ptr, ','); /* exit condition */
}
rfilter = malloc(sizeof(struct can_filter) * numfilter);
if (!rfilter) {
fprintf(stderr, "Failed to create filter space!\n");
return 1;
}
numfilter = 0;
err_mask = 0;
join_filter = 0;
while (nptr) {
ptr = nptr+1; /* hop behind the ',' */
nptr = strchr(ptr, ','); /* update exit condition */
if (sscanf(ptr, "%x:%x",
&rfilter[numfilter].can_id,
&rfilter[numfilter].can_mask) == 2) {
rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG;
numfilter++;
} else if (sscanf(ptr, "%x~%x",
&rfilter[numfilter].can_id,
&rfilter[numfilter].can_mask) == 2) {
rfilter[numfilter].can_id |= CAN_INV_FILTER;
rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG;
numfilter++;
} else if (*ptr == 'j' || *ptr == 'J') {
join_filter = 1;
} else if (sscanf(ptr, "#%x", &err_mask) != 1) {
fprintf(stderr, "Error in filter option parsing: '%s'\n", ptr);
return 1;
}
}
if (err_mask)
setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
&err_mask, sizeof(err_mask));
if (join_filter && setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS,
&join_filter, sizeof(join_filter)) < 0) {
perror("setsockopt CAN_RAW_JOIN_FILTERS not supported by your Linux Kernel");
return 1;
}
if (numfilter)
setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FILTER,
rfilter, numfilter * sizeof(struct can_filter));
free(rfilter);
} /* if (nptr) */
/* try to switch the socket into CAN FD mode */
setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on));
if (rcvbuf_size) {
int curr_rcvbuf_size;
socklen_t curr_rcvbuf_size_len = sizeof(curr_rcvbuf_size);
/* try SO_RCVBUFFORCE first, if we run with CAP_NET_ADMIN */
if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUFFORCE,
&rcvbuf_size, sizeof(rcvbuf_size)) < 0) {
#ifdef DEBUG
printf("SO_RCVBUFFORCE failed so try SO_RCVBUF ...\n");
#endif
if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUF,
&rcvbuf_size, sizeof(rcvbuf_size)) < 0) {
perror("setsockopt SO_RCVBUF");
return 1;
}
if (getsockopt(s[i], SOL_SOCKET, SO_RCVBUF,
&curr_rcvbuf_size, &curr_rcvbuf_size_len) < 0) {
perror("getsockopt SO_RCVBUF");
return 1;
}
/* Only print a warning the first time we detect the adjustment */
/* n.b.: The wanted size is doubled in Linux in net/sore/sock.c */
if (!i && curr_rcvbuf_size < rcvbuf_size*2)
fprintf(stderr, "The socket receive buffer size was "
"adjusted due to /proc/sys/net/core/rmem_max.\n");
}
}
if (timestamp || log || logfrmt) {
if (hwtimestamp) {
const int timestamping_flags = (SOF_TIMESTAMPING_SOFTWARE | \
SOF_TIMESTAMPING_RX_SOFTWARE | \
SOF_TIMESTAMPING_RAW_HARDWARE);
if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMPING,
&timestamping_flags, sizeof(timestamping_flags)) < 0) {
perror("setsockopt SO_TIMESTAMPING is not supported by your Linux kernel");
return 1;
}
} else {
const int timestamp_on = 1;
if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMP,
&timestamp_on, sizeof(timestamp_on)) < 0) {
perror("setsockopt SO_TIMESTAMP");
return 1;
}
}
}
if (dropmonitor) {
const int dropmonitor_on = 1;
if (setsockopt(s[i], SOL_SOCKET, SO_RXQ_OVFL,
&dropmonitor_on, sizeof(dropmonitor_on)) < 0) {
perror("setsockopt SO_RXQ_OVFL not supported by your Linux Kernel");
return 1;
}
}
if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}
}
if (log) {
time_t currtime;
struct tm now;
char fname[sizeof("candump-2006-11-20_202026.log")+1];
if (time(&currtime) == (time_t)-1) {
perror("time");
return 1;
}
localtime_r(&currtime, &now);
sprintf(fname, "candump-%04d-%02d-%02d_%02d%02d%02d.log",
now.tm_year + 1900,
now.tm_mon + 1,
now.tm_mday,
now.tm_hour,
now.tm_min,
now.tm_sec);
if (silent != SILENT_ON)
printf("\nWarning: console output active while logging!");
fprintf(stderr, "\nEnabling Logfile '%s'\n\n", fname);
logfile = fopen(fname, "w");
if (!logfile) {
perror("logfile");
return 1;
}
}
/* these settings are static and can be held out of the hot path */
iov.iov_base = &frame;
msg.msg_name = &addr;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &ctrlmsg;
while (running) {
/*------------------------------- Opening the Serial Port -------------------------------*/
/* Change /dev/ttyUSB0 to the one corresponding to your system */
fd = open("/dev/ttyO2",O_RDWR | O_NOCTTY); /* ttyUSB0 is the FT232 based USB2SERIAL Converter */
/* O_RDWR - Read/Write access to serial port */
/* O_NOCTTY - No terminal will control the process */
/* Open in blocking mode,read will wait */
/* Error Checking */
if(fd == -1)
printf("\n Error! in Opening ttyO2 ");
else
printf("\n ttyO2 Opened Successfully ");
/*---------- Setting the Attributes of the serial port using termios structure --------- */
//struct termios SerialPortSettings; /* Create the structure */
tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */
/* Setting the Baud rate */
cfsetispeed(&SerialPortSettings,B9600); /* Set Read Speed as 9600 */
cfsetospeed(&SerialPortSettings,B9600); /* Set Write Speed as 9600 */
/* 8N1 Mode */
SerialPortSettings.c_cflag &= ~PARENB; /* Disables the Parity Enable bit(PARENB),So No Parity */
SerialPortSettings.c_cflag &= ~CSTOPB; /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
SerialPortSettings.c_cflag &= ~CSIZE; /* Clears the mask for setting the data size */
SerialPortSettings.c_cflag |= CS8; /* Set the data bits = 8 */
SerialPortSettings.c_cflag &= ~CRTSCTS; /* No Hardware flow Control */
SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines */
SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF flow control both i/p and o/p */
SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* Non Cannonical mode */
SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/
/* Setting Time outs */
SerialPortSettings.c_cc[VMIN] = 42; /* Read at least 42 characters */
SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly */
if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
printf("\n ERROR ! in Setting attributes");
else
printf("\n BaudRate = 9600 \n StopBits = 1 \n Parity = none \n\n");
/*------------------------------- Read data from serial port -----------------------------*/
tcflush(fd, TCIFLUSH); /* Discards old data in the rx buffer */
char read_buffer[42]; /* Buffer to store the data received */
int bytes_read = 0; /* Number of bytes read by the read() system call */
int ia = 0; int a;
int test = 0;
char new_read[38];
char curr_read[33];
int countc = 0;
a = 0;
do{
bytes_read = read(fd,&read_buffer,42); /* Read the data */
if(read_buffer[0] == '$')
if(read_buffer[1] == 'G')
if(read_buffer[2] == 'P')
if(read_buffer[3] == 'G')
if(read_buffer[4] == 'G'){
for(ia=7;ia<bytes_read;ia++){ /*printing only the received characters*/
new_read[a] = read_buffer[ia];
//printf("%c",read_buffer[ia]);
a = a+1;
test = 1;
}
strcpy(curr_read, new_read);
//printf("\n%s \n", curr_read);
}
else
test = 0;
else
test = 0;
else
test = 0;
else
test = 0;
else
test = 0;
}while(test == 0);
//tcflush(fd, TCIFLUSH); /* Discards old data in the rx buffer */
close(fd); /* Close the serial port */
while(countc < 1000){
FD_ZERO(&rdfs);
for (i=0; i<currmax; i++)
FD_SET(s[i], &rdfs);
if (timeout_current)
*timeout_current = timeout_config;
if ((ret = select(s[currmax-1]+1, &rdfs, NULL, NULL, timeout_current)) <= 0) {
//perror("select");
running = 0;
continue;
}
for (i=0; i<currmax; i++) { /* check all CAN RAW sockets */
if (FD_ISSET(s[i], &rdfs)) {
int idx;
/* these settings may be modified by recvmsg() */
iov.iov_len = sizeof(frame);
msg.msg_namelen = sizeof(addr);
msg.msg_controllen = sizeof(ctrlmsg);
msg.msg_flags = 0;
nbytes = recvmsg(s[i], &msg, 0);
idx = idx2dindex(addr.can_ifindex, s[i]);
if (nbytes < 0) {
if ((errno == ENETDOWN) && !down_causes_exit) {
fprintf(stderr, "%s: interface down\n", devname[idx]);
continue;
}
perror("read");
return 1;
}
if ((size_t)nbytes == CAN_MTU)
maxdlen = CAN_MAX_DLEN;
else if ((size_t)nbytes == CANFD_MTU)
maxdlen = CANFD_MAX_DLEN;
else {
fprintf(stderr, "read: incomplete CAN frame\n");
return 1;
}
if (count && (--count == 0))
running = 0;
if (bridge) {
if (bridge_delay)
usleep(bridge_delay);
nbytes = write(bridge, &frame, nbytes);
if (nbytes < 0) {
perror("bridge write");
return 1;
} else if ((size_t)nbytes != CAN_MTU && (size_t)nbytes != CANFD_MTU) {
fprintf(stderr,"bridge write: incomplete CAN frame\n");
return 1;
}
}
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg && (cmsg->cmsg_level == SOL_SOCKET);
cmsg = CMSG_NXTHDR(&msg,cmsg)) {
if (cmsg->cmsg_type == SO_TIMESTAMP) {
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
} else if (cmsg->cmsg_type == SO_TIMESTAMPING) {
struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg);
/*
* stamp[0] is the software timestamp
* stamp[1] is deprecated
* stamp[2] is the raw hardware timestamp
* See chapter 2.1.2 Receive timestamps in
* linux/Documentation/networking/timestamping.txt
*/
tv.tv_sec = stamp[2].tv_sec;
tv.tv_usec = stamp[2].tv_nsec/1000;
} else if (cmsg->cmsg_type == SO_RXQ_OVFL)
memcpy(&dropcnt[i], CMSG_DATA(cmsg), sizeof(__u32));
}
/* check for (unlikely) dropped frames on this specific socket */
if (dropcnt[i] != last_dropcnt[i]) {
__u32 frames = dropcnt[i] - last_dropcnt[i];
if (silent != SILENT_ON)
printf("DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n",
frames, (frames > 1)?"s":"", devname[idx], dropcnt[i]);
if (log)
fprintf(logfile, "DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n",
frames, (frames > 1)?"s":"", devname[idx], dropcnt[i]);
last_dropcnt[i] = dropcnt[i];
}
/* once we detected a EFF frame indent SFF frames accordingly */
if (frame.can_id & CAN_EFF_FLAG)
view |= CANLIB_VIEW_INDENT_SFF;
if (log) { /* CODE GETS IN TO THIS PART */
char buf[CL_CFSZ]; /* max length */ /* WHEN PRINTING INTO FILE */
/* */
/* log CAN frame with absolute timestamp & device */ /* */
sprint_canframe(buf, &frame, 0, maxdlen); /* */
fprintf(logfile, "%s %*s %s\n", /* */
curr_read, /* */
max_devname_len, devname[idx], buf); /* */
} /* */
if (logfrmt) {
char buf[CL_CFSZ]; /* max length */
/* print CAN frame in log file style to stdout */
sprint_canframe(buf, &frame, 0, maxdlen);
printf("(%010ld.%06ld) %*s %s\n",
tv.tv_sec, tv.tv_usec,
max_devname_len, devname[idx], buf);
goto out_fflush; /* no other output to stdout */
}
if (silent != SILENT_OFF){
if (silent == SILENT_ANI) {
printf("%c\b", anichar[silentani%=MAXANI]);
silentani++;
}
goto out_fflush; /* no other output to stdout */
}
printf(" %s", (color>2)?col_on[idx%MAXCOL]:"");
switch (timestamp) {
case 'a': /* absolute with timestamp */
printf("(%010ld.%06ld) ", tv.tv_sec, tv.tv_usec);
break;
case 'A': /* absolute with date */
{
struct tm tm;
char timestring[25];
tm = *localtime(&tv.tv_sec);
strftime(timestring, 24, "%Y-%m-%d %H:%M:%S", &tm);
printf("(%s.%06ld) ", timestring, tv.tv_usec);
}
break;
case 'd': /* delta */
case 'z': /* starting with zero */
{
struct timeval diff;
if (last_tv.tv_sec == 0) /* first init */
last_tv = tv;
diff.tv_sec = tv.tv_sec - last_tv.tv_sec;
diff.tv_usec = tv.tv_usec - last_tv.tv_usec;
if (diff.tv_usec < 0)
diff.tv_sec--, diff.tv_usec += 1000000;
if (diff.tv_sec < 0)
diff.tv_sec = diff.tv_usec = 0;
printf("(%03ld.%06ld) ", diff.tv_sec, diff.tv_usec);
if (timestamp == 'd')
last_tv = tv; /* update for delta calculation */
}
break;
default: /* no timestamp output */
break;
}
printf(" %s", (color && (color<3))?col_on[idx%MAXCOL]:"");
printf("%*s", max_devname_len, devname[idx]);
if (extra_msg_info) {
if (msg.msg_flags & MSG_DONTROUTE)
printf (" TX %s", extra_m_info[frame.flags & 3]);
else
printf (" RX %s", extra_m_info[frame.flags & 3]);
}
printf("%s ", (color==1)?col_off:"");
fprint_long_canframe(stdout, &frame, NULL, view, maxdlen);
printf("%s", (color>1)?col_off:"");
printf("\n");
}
out_fflush:
fflush(stdout);
}
countc = countc +1;
}
}
for (i=0; i<currmax; i++)
close(s[i]);
if (bridge)
close(bridge);
if (log)
fclose(logfile);
return 0;
}
Actually everything matters works in while(running) block. Inside this block when I make the bytes_read = read(fd,&read_buffer,42); as comment, it didn't write anything but also doesn't give the Segmentation fault error. Same also happens when I connect the GPS' TX pin in to BBB. So the problem starts to occur when the data is coming from the GPS and read by the BBB.
Segmentation Fault Err
What should I do about it?
Thanks.
Your GPS reading code
char new_read[38];
char curr_read[33];
strcpy(curr_read, new_read);
is copying a 38 char buffer into a 33 char buffer, which can result in bad things.
Strcpy will copy the contents of the source buffer into the destination buffer until it reads NULL from the source buffer. If the NULL char is at the 36th position in new_read, strcpy will be writing in random memory which can cause the segmentation fault.
I am guessing that when you run your GPS reading code as stand-alone, the writing into random memory goes un-noticed, but when you combine it with the CAN bus reading, it writes into allocated space and the error happens.

STM32 HAL_UART_Transmit dynamic string

I'm trying to send a variable length string via UART, using HAL function.
There is no way to send a string that is changing its length runtime, I have tried with various declarations, inside and outside while loop, but if I don't declare a fix length string (char buffer[30] for example), HAL is not taking it.
char buffer; char buffer[] = "", even char buffer = malloc(sizeof(char)), nothing is working. I have on terminal only some character or nothing.
Is there a way to pass a string that is variable in length to HAL?
Thanks.
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
uint32_t index1 = 0, index2 = 0;
const char message[] = "Hello from Nucleo64";
const char message2[] = "Pressed!";
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin)){
char *buffer;
sprintf(buffer, "%s - index=%d\n", message, index1);
HAL_UART_Transmit(&huart2, (uint8_t *)buffer, sizeof(buffer)-1, 10);
index1 += 1;
} else
{
char *buffer2;
sprintf(buffer2, "%s - index=%d\n", message2, index2);
HAL_UART_Transmit(&huart2, (uint8_t *)buffer2, sizeof(buffer2)-1, 10);
index2 += 1;
}
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
HAL_Delay(500);
}
/* USER CODE END 3 */
}
As suggested, I tryed also snprintf, on the terminal I see nothing but [00] when I reset, then nothing is transmitted.
uint32_t index1 = 0, index2 = 0;
char message[] = "Hello from Nucleo64";
char message2[] = "Pressed!";
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin)){
size_t needed = snprintf(NULL, 0, "%s - index=%d\n", message, index1) + 1;
char *buffer = malloc(needed);
snprintf(buffer, needed, "%s - index=%d\n", message, index1);
HAL_UART_Transmit(&huart2, (uint8_t *)buffer, strlen(buffer), 10);
index1 += 1;
free(buffer);
} else
{
size_t needed = snprintf(NULL, 0, "%s - index=%d\n", message2, index2) + 1;
char *buffer2 = malloc(needed);
snprintf(buffer2, needed, "%s - index=%d\n", message2, index2);
HAL_UART_Transmit(&huart2, (uint8_t *)buffer2, strlen(buffer2), 10);
index2 += 1;
free(buffer2);
}
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
HAL_Delay(500);
}
/* USER CODE END 3 */
}
SOLVED *****************************
Used printf after declaring prorotype GNUC
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
and passing it to HAL_UART_Trasmit
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}

accessing i2c platform device from userspace program

I'm trying to access an 24c256 eeprom content from user space in a am335x_starter_kit.
I dont have to add eeprom driver into kernel and make modifications in board.c file because board already uses eeprom to access some board configuration and Mac address information.
I just want to access eeprom content from user space.
I used read and write functions for character devices before but i2c platform devices doesnt have these functions.
struct i2c_driver {
unsigned int class;
int (* attach_adapter) (struct i2c_adapter *);
int (* probe) (struct i2c_client *, const struct i2c_device_id *);
int (* remove) (struct i2c_client *);
void (* shutdown) (struct i2c_client *);
void (* alert) (struct i2c_client *, unsigned int data);
int (* command) (struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id * id_table;
int (* detect) (struct i2c_client *, struct i2c_board_info *);
const unsigned short * address_list;
struct list_head clients;
};
This is the eeprom driver. Board file uses it from kernel to get mac address and board configuration data.
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/i2c/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
* MicroChip 24LC, etc) won't much matter for typical read/write access.
* There are also I2C RAM chips, likewise interchangeable. One example
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
*
* However, misconfiguration can lose data. "Set 16-bit memory address"
* to a part with 8-bit addressing will overwrite data. Writing with too
* big a page size also loses data. And it's not safe to assume that the
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
* uses 0x51, for just one example.
*
* Accordingly, explicit board-specific configuration data should be used
* in almost all cases. (One partial exception is an SMBus used to access
* "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
*
* So this driver uses "new style" I2C driver binding, expecting to be
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
* similar kernel-resident tables; or, configuration data coming from
* a bootloader.
*
* Other than binding model, current differences from "eeprom" driver are
* that this one handles write access and isn't restricted to 24c02 devices.
* It also handles larger devices (32 kbit and up) with two-byte addresses,
* which won't work on pure SMBus systems.
*/
struct at24_data {
struct at24_platform_data chip;
struct memory_accessor macc;
int use_smbus;
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
*/
struct mutex lock;
struct bin_attribute bin;
u8 *writebuf;
unsigned write_max;
unsigned num_addresses;
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
* them for us, and we'll use them with SMBus calls.
*/
struct i2c_client *client[];
};
/*
* This parameter is to help this driver avoid blocking other drivers out
* of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
* clock, one 256 byte read takes about 1/43 second which is excessive;
* but the 1/170 second it takes at 400 kHz may be quite reasonable; and
* at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
*
* This value is forced to be a power of two so that writes align on pages.
*/
static unsigned io_limit = 128;
module_param(io_limit, uint, 0);
MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
/*
* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it's important to recover from write timeouts.
*/
static unsigned write_timeout = 25;
module_param(write_timeout, uint, 0);
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
#define AT24_SIZE_BYTELEN 5
#define AT24_SIZE_FLAGS 8
#define AT24_BITMASK(x) (BIT(x) - 1)
/* create non-zero magic value for given eeprom parameters */
#define AT24_DEVICE_MAGIC(_len, _flags) \
((1 << AT24_SIZE_FLAGS | (_flags)) \
<< AT24_SIZE_BYTELEN | ilog2(_len))
static const struct i2c_device_id at24_ids[] = {
/* needs 8 addresses as A0-A2 are ignored */
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
/* old variants can't be handled with this generic entry! */
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
/* spd is a 24c02 in memory DIMMs */
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
/* 24rf08 quirk is handled at i2c-core */
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
{ "at24", 0 },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ids);
/*-------------------------------------------------------------------------*/
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
* Assumes that sanity checks for offset happened at sysfs-layer.
*/
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
unsigned *offset)
{
unsigned i;
if (at24->chip.flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
i = *offset >> 8;
*offset &= 0xff;
}
return at24->client[i];
}
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
unsigned offset, size_t count)
{
struct i2c_msg msg[2];
u8 msgbuf[2];
struct i2c_client *client;
unsigned long timeout, read_time;
int status, i;
memset(msg, 0, sizeof(msg));
/*
* REVISIT some multi-address chips don't rollover page reads to
* the next slave address, so we may need to truncate the count.
* Those chips might need another quirk flag.
*
* If the real hardware used four adjacent 24c02 chips and that
* were misconfigured as one 24c08, that would be a similar effect:
* one "eeprom" file not four, but larger reads would fail when
* they crossed certain pages.
*/
/*
* Slave address and byte offset derive from the offset. Always
* set the byte address; on a multi-master board, another master
* may have changed the chip's "current" address pointer.
*/
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
switch (at24->use_smbus) {
case I2C_SMBUS_I2C_BLOCK_DATA:
/* Smaller eeproms can work given some SMBus extension calls */
if (count > I2C_SMBUS_BLOCK_MAX)
count = I2C_SMBUS_BLOCK_MAX;
break;
case I2C_SMBUS_WORD_DATA:
count = 2;
break;
case I2C_SMBUS_BYTE_DATA:
count = 1;
break;
default:
/*
* When we have a better choice than SMBus calls, use a
* combined I2C message. Write address; then read up to
* io_limit data bytes. Note that read page rollover helps us
* here (unlike writes). msgbuf is u8 and will cast to our
* needs.
*/
i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msgbuf[i++] = offset >> 8;
msgbuf[i++] = offset;
msg[0].addr = client->addr;
msg[0].buf = msgbuf;
msg[0].len = i;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;
}
/*
* Reads fail if the previous write didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
read_time = jiffies;
switch (at24->use_smbus) {
case I2C_SMBUS_I2C_BLOCK_DATA:
status = i2c_smbus_read_i2c_block_data(client, offset,
count, buf);
break;
case I2C_SMBUS_WORD_DATA:
status = i2c_smbus_read_word_data(client, offset);
if (status >= 0) {
buf[0] = status & 0xff;
buf[1] = status >> 8;
status = count;
}
break;
case I2C_SMBUS_BYTE_DATA:
status = i2c_smbus_read_byte_data(client, offset);
if (status >= 0) {
buf[0] = status;
status = count;
}
break;
default:
status = i2c_transfer(client->adapter, msg, 2);
if (status == 2)
status = count;
}
dev_dbg(&client->dev, "read %zu#%d --> %d (%ld)\n",
count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(read_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Read data from chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_read(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_read(at24, buf, off, count);
}
/*
* Note that if the hardware write-protect pin is pulled high, the whole
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
* We only use page mode writes; the alternative is sloooow. This routine
* writes at most one page.
*/
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
unsigned offset, size_t count)
{
struct i2c_client *client;
struct i2c_msg msg;
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
/* Get corresponding I2C address and adjust offset */
client = at24_translate_offset(at24, &offset);
/* write_max is at most a page */
if (count > at24->write_max)
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
next_page = roundup(offset + 1, at24->chip.page_size);
if (offset + count > next_page)
count = next_page - offset;
/* If we'll use I2C calls for I/O, set up the message */
if (!at24->use_smbus) {
int i = 0;
msg.addr = client->addr;
msg.flags = 0;
/* msg.buf is u8 and casts will mask the values */
msg.buf = at24->writebuf;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msg.buf[i++] = offset >> 8;
msg.buf[i++] = offset;
memcpy(&msg.buf[i], buf, count);
msg.len = i + count;
}
/*
* Writes fail if the previous one didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
write_time = jiffies;
if (at24->use_smbus) {
status = i2c_smbus_write_i2c_block_data(client,
offset, count, buf);
if (status == 0)
status = count;
} else {
status = i2c_transfer(client->adapter, &msg, 1);
if (status == 1)
status = count;
}
dev_dbg(&client->dev, "write %zu#%d --> %zd (%ld)\n",
count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(write_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Write data to chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_write(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
/*-------------------------------------------------------------------------*/
/*
* This lets other kernel code access the eeprom data. For example, it
* might hold a board's Ethernet address, or board-specific calibration
* data generated on the manufacturing floor.
*/
static ssize_t at24_macc_read(struct memory_accessor *macc, char *buf,
off_t offset, size_t count)
{
struct at24_data *at24 = container_of(macc, struct at24_data, macc);
return at24_read(at24, buf, offset, count);
}
static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf,
off_t offset, size_t count)
{
struct at24_data *at24 = container_of(macc, struct at24_data, macc);
return at24_write(at24, buf, offset, count);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{
const __be32 *val;
struct device_node *node = client->dev.of_node;
if (node) {
if (of_get_property(node, "read-only", NULL))
chip->flags |= AT24_FLAG_READONLY;
val = of_get_property(node, "pagesize", NULL);
if (val)
chip->page_size = be32_to_cpup(val);
}
}
#else
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{ }
#endif /* CONFIG_OF */
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
int use_smbus = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
kernel_ulong_t magic;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
if (!id->driver_data) {
err = -ENODEV;
goto err_out;
}
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
/*
* This is slow, but we can't know all eeproms, so we better
* play safe. Specifying custom eeprom-types via platform_data
* is recommended anyhow.
*/
chip.page_size = 1;
/* update chipdata if OF is present */
at24_get_ofdata(client, &chip);
chip.setup = NULL;
chip.context = NULL;
}
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
err = -EINVAL;
goto err_out;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (chip.flags & AT24_FLAG_ADDR16) {
err = -EPFNOSUPPORT;
goto err_out;
}
if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
use_smbus = I2C_SMBUS_WORD_DATA;
} else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
err = -EPFNOSUPPORT;
goto err_out;
}
}
//???????????????
if (chip.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
num_addresses = DIV_ROUND_UP(chip.byte_len, (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
at24 = kzalloc(sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
if (!at24) {
err = -ENOMEM;
goto err_out;
}
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
at24->chip = chip;
at24->num_addresses = num_addresses;
/*
* Export the EEPROM bytes through sysfs, since that's convenient.
* By default, only root should see the data (maybe passwords etc)
*/
sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
at24->macc.read = at24_macc_read;
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
unsigned write_max = chip.page_size;
at24->macc.write = at24_macc_write;
at24->bin.write = at24_bin_write;
at24->bin.attr.mode |= S_IWUSR;
if (write_max > io_limit)
write_max = io_limit;
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
write_max = I2C_SMBUS_BLOCK_MAX;
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
if (!at24->writebuf) {
err = -ENOMEM;
goto err_struct;
}
} else {
dev_warn(&client->dev,
"cannot write due to controller restrictions.");
}
}
at24->client[0] = client;
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
at24->client[i] = i2c_new_dummy(client->adapter,
client->addr + i);
if (!at24->client[i]) {
dev_err(&client->dev, "address 0x%02x unavailable\n",
client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
}
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if (err)
goto err_clients;
i2c_set_clientdata(client, at24);
dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n", at24->bin.size, client->name,
writable ? "writable" : "read-only", at24->write_max);
if (use_smbus == I2C_SMBUS_WORD_DATA ||
use_smbus == I2C_SMBUS_BYTE_DATA) {
dev_notice(&client->dev, "Falling back to %s reads, "
"performance will suffer\n", use_smbus ==
I2C_SMBUS_WORD_DATA ? "word" : "byte");
}
/* export data to kernel code */
if (chip.setup)
chip.setup(&at24->macc, chip.context);
return 0;
err_clients:
for (i = 1; i < num_addresses; i++)
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
kfree(at24->writebuf);
err_struct:
kfree(at24);
err_out:
dev_dbg(&client->dev, "probe error %d\n", err);
return err;
}
/*-------------------------------------------------------------------------*/
static int __devexit at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
at24 = i2c_get_clientdata(client);
sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
kfree(at24->writebuf);
kfree(at24);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};
static int __init at24_init(void)
{
if (!io_limit) {
pr_err("at24: io_limit must not be 0!\n");
return -EINVAL;
}
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver);
}
module_init(at24_init);
static void __exit at24_exit(void)
{
i2c_del_driver(&at24_driver);
}
module_exit(at24_exit);
MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
MODULE_AUTHOR("David Brownell and Wolfram Sang");
MODULE_LICENSE("GPL");
These are snippets from board file:
static struct i2c_board_info __initdata am335x_i2c0_boardinfo[] = {
{
/* Baseboard board EEPROM */
I2C_BOARD_INFO("24c256", BASEBOARD_I2C_ADDR),
.platform_data = &am335x_baseboard_eeprom_info,
},
.
.
static struct at24_platform_data am335x_baseboard_eeprom_info = {
.byte_len = (256*1024) / 8,
.page_size = 64,
.flags = AT24_FLAG_ADDR16,
.setup = am335x_evm_setup,
.context = (void *)NULL,
};
static void am335x_evm_setup(struct memory_accessor *mem_acc, void *context)
{
int ret;
char tmp[10];
struct device *mpu_dev;
/* 1st get the MAC address from EEPROM */
ret = mem_acc->read(mem_acc, (char *)&am335x_mac_addr,
EEPROM_MAC_ADDRESS_OFFSET, sizeof(am335x_mac_addr));
.
.
.
How can i read from/write into eeprom content from user space.
Should i use sysfs? What should i do?
EEPROM:
It's part of setting the MAC and serial number, but the only way to know if the EEPROM is working is to read its content.
$ cat /sys/bus/i2c/devices/2-0057/eeprom | hexdump -C

BeagleBoneBlack - Register I2S ADC on ALSA

i'm trying to interface an audio ADC (wm8782/pcm1803a) with the Beagle Black. I already did some changes in the files davinci-evm , wm8782.c and BB-BONE-AUDI-01 device tree overlay file (see code below).
My problem is that when loading the dtbo file dmesg returns:
"...Codec DAI wm8782-hifi not found"
I'm assuming that my codec (wm8782) is not being registered by alsa core, but where I have to do that??
I'm running Ubuntu 13.10, kernel: 3.8.13-bone39
Thanks!
davinci-evm.c
/*
* ASoC Driver.
*
*
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
static int evm_wm8782_init(struct snd_soc_pcm_runtime *rtd)
{
return 0;
}
static int evm_wm8782_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
/*return snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2);*/
return 0;
}
/* machine stream operations */
static struct snd_soc_ops evm_wm8782_ops = {
.hw_params = evm_wm8782_hw_params,
};
static struct snd_soc_dai_link evm_wm8782_dai[] = {
{
.name = "PCM1803 board",
.stream_name = "PCM1803 board",
.cpu_dai_name = "davinci-mcasp.0",
.codec_dai_name = "wm8782-hifi",
.platform_name = "davinci-pcm-audio",
.codec_name = "wm8782-codec",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &evm_wm8782_ops,
.init = evm_wm8782_init,
},
};
/* audio machine driver */
static struct snd_soc_card snd_wm8782_adc = {
.name = "snd_wm8782_adc",
.dai_link = evm_wm8782_dai,
.num_links = ARRAY_SIZE(evm_wm8782_dai),
};
static int snd_wm8782_probe(struct platform_device *pdev)
{
int ret = 0;
snd_wm8782_adc.dev = &pdev->dev;
ret = snd_soc_register_card(&snd_wm8782_adc);
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret);
return ret;
}
static int snd_wm8782_remove(struct platform_device *pdev)
{
return snd_soc_unregister_card(&snd_wm8782_adc);
}
static struct platform_driver snd_wm8782_driver = {
.driver = {
.name = "snd-wm8782-adc",
.owner = THIS_MODULE,
},
.probe = snd_wm8782_probe,
.remove = snd_wm8782_remove,
};
module_platform_driver(snd_wm8782_driver);
MODULE_AUTHOR("E.E.R");
MODULE_DESCRIPTION("ASoC Driver for pcm1803 ADC");
MODULE_LICENSE("GPL v2");
wm8782.c
/*
* sound/soc/codecs/wm8782.c
* simple, strap-pin configured 24bit 2ch ADC
*
* Copyright: 2011 Raumfeld GmbH
* Author: Johannes Stezenbach <js#sig21.net>
*
* based on ad73311.c
* Copyright: Analog Device Inc.
* Author: Cliff Cai <cliff.cai#analog.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
static struct snd_soc_dai_driver wm8782_dai = {
.name = "wm8782-hifi",
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
};
static struct snd_soc_codec_driver soc_codec_dev_wm8782;
static int wm8782_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_wm8782, &wm8782_dai, 1);
}
static int wm8782_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
static struct platform_driver wm8782_codec_driver = {
.driver = {
.name = "wm8782",
.owner = THIS_MODULE,
},
.probe = wm8782_probe,
.remove = wm8782_remove,
};
module_platform_driver(wm8782_codec_driver);
MODULE_DESCRIPTION("ASoC WM8782 driver");
MODULE_AUTHOR("Johannes Stezenbach <js#sig21.net>");
MODULE_LICENSE("GPL");
BB-BONE-AUDI-01-00A0.dts
/*
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/dts-v1/;
/plugin/;
/ {
compatible = "ti,beaglebone", "ti,beaglebone-black";
/* identification */
part-number = "BB-BONE-AUDI-01";
version = "00A0", "A0";
/* state the resources this cape uses */
exclusive-use =
/* the pin header uses */
"P9.14", /* leds: gpio1_18 */
"P9.16", /* leds: gpio1_19 */
"P9.31", /* mcasp0: mcasp0_aclkx */
"P9.29", /* mcasp0: mcasp0_fsx */
"P9.28", /* mcasp0: mcasp0_axr2 */
"P9.25", /* mcasp0: mcasp0_ahclkx */
/* the hardware ip uses */
"gpio1_18", "gpio1_19",
"mcasp0";
fragment#0 {
target = <&am33xx_pinmux>;
__overlay__ {
bone_audio_cape_led_pins: pinmux_bone_audio_cape_led_pins {
pinctrl-single,pins = <
0x48 0x07 /* gpmc_a2.gpio1_18, OUTPUT | MODE7 */
0x4c 0x07 /* gpmc_a3.gpio1_19, OUTPUT | MODE7 */
>;
};
bone_audio_cape_audio_pins: pinmux_bone_audio_cape_audio_pins {
pinctrl-single,pins = <
0x190 0x20 /* mcasp0_aclkx.mcasp0_aclkx, INPUT | MODE0 */
0x194 0x20 /* mcasp0_fsx.mcasp0_fsx, INPUT | MODE0 */
0x19c 0x22 /* mcasp0_ahclkr.mcasp0_axr2, INPUT | MODE2 */
0x1ac 0x22 /* mcasp0_ahclkx.mcasp0_axr3, INPUT | MODE2 */
>;
};
};
};
fragment#1 {
target = <&ocp>;
__overlay__ {
/* avoid stupid warning */
#address-cells = <1>;
#size-cells = <1>;
gpio-leds-cape-audio {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&bone_audio_cape_led_pins>;
audio-led0 {
label = "audio:green:usr0";
gpios = <&gpio2 18 0>;
linux,default-trigger = "heartbeat";
default-state = "off";
};
audio-led1 {
label = "audio:green:usr1";
gpios = <&gpio2 19 0>;
linux,default-trigger = "mmc0";
default-state = "off";
};
};
};
};
fragment#2 {
target = <&mcasp0>;
__overlay__ {
pinctrl-names = "default";
pinctrl-0 = <&bone_audio_cape_audio_pins>;
status = "okay";
op-mode = <0>; /* MCASP_IIS_MODE */
tdm-slots = <2>;
num-serializer = <16>;
serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
0 0 2 1
0 0 0 0
0 0 0 0
0 0 0 0
>;
tx-num-evt = <1>;
rx-num-evt = <1>;
};
};
};
You can make your codec driver becomes Device Tree compatible and to load that simply add a node for the codec in your dts file.
In your case, add this part to your codec driver:
.
.
.
#ifdef CONFIG_OF
static const struct of_device_id wm8782_dt_ids[] = {
{ .compatible = "ti,wm8782", },
{ }
};
MODULE_DEVICE_TABLE(of, wm8782_dt_ids);
#endif
static struct platform_driver wm8782_codec_driver = {
.driver = {
.name = "wm8782",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(wm8782_dt_ids),
},
.
.
.
Then, add this node to your dts file:
wm8782: wm8782 {
wm8782 = "ti,wm8782";
};

Custom ALSA driver does not execute the open functions from struct snd_pcm_ops

I am trying to create my own custom sound driver using ALSA. So far I have succeeded loading the module, by which I mean:
my probe function is executed
the constructor is executed
I see the interrupt being executed
I found this out by the dumps I placed in these functions.
But when I try to use the snd_pcm_open() function in my application, to connect to my driver, I do not see the dumps I added in the .open() function stored in struct snd_pcm_ops. So for some reason my driver is not seen from my application.
I loaded the original driver and executed the same application and it worked fine.
Do you have any idea why my driver is no accessible from user space?
Thank you.
And here is the code:
#include <linux/fs.h>
#include <linux/module.h> // MOD_DEVICE_TABLE,
#include <linux/init.h>
#include <linux/pci.h> // pci_device_id,
#include <linux/interrupt.h>
#include <linux/version.h> // KERNEL_VERSION,
#include <iso646.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h> // copy_to_user,
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/time.h>
MODULE_LICENSE("GPL");
// vendor and device id of the PCI device
#define VENDOR_ID 0x8086
#define DEVICE_ID 0x2415
/*************** DATA **********************/
/* module parameters (see "Module Parameters") */
/* SNDRV_CARDS: maximum number of cards supported by this module */
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
#define BARS 6
enum bars{bar0=0, bar1=1, bar2=2, bar3=3, bar4=4, bar5=5};
#define IOREG_0 0
#define IOREG_1 1
/*OFFSETS TO REGISTER*/
#define GLOBAL_STATUS_REGISTER_OFFSET 0x30
struct mem_regions
{
unsigned int start_addr;
unsigned int end_addr;
unsigned int mem_len;
unsigned int bar;
unsigned int flag;
void __iomem *mem_mapped_addr;
};
struct mychip {
struct snd_card *card;
struct pci_dev *pci;
unsigned long port;
int irq;
struct mem_regions memregs[6];
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
};
/*******************************************/
/******************** FUNCTION PROTOTYPES *********************************/
static int __devinit snd_mychip_create(struct snd_card *card,
struct pci_dev *pci,
struct mychip **rchip);
static int snd_mychip_free(struct mychip *chip);
static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id);
static int __devinit snd_mychip_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id);
static void snd_mychip_remove(struct pci_dev *pci);
static int snd_mychip_dev_free(struct snd_device *device);
static int reserve_mem_regions(struct mychip *chip);
static void clear_mem_regions(struct mychip *chip);
static void memdump(struct mychip *chip);
/* ------------ PCM functions ------------- */
/* ---- constructor ---- */
static int __devinit snd_pcm_constructor(struct mychip *chip);
/* ---- destructor ---- */
static void snd_pcm_destructor(struct snd_pcm *pcm);
/* ---- file operations fcns ---- */
static int snd_pcm_playback_open(struct snd_pcm_substream *substream);
static int snd_pcm_playback_close(struct snd_pcm_substream *substream);
static int snd_pcm_capture_open(struct snd_pcm_substream *substream);
static int snd_pcm_capture_close(struct snd_pcm_substream *substream);
/*snd_pcm_lib_ioctl();*/
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params);
static int snd_pcm_hw_free(struct snd_pcm_substream *substream);
static int snd_pcm_prepare(struct snd_pcm_substream *substream);
static int snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
static snd_pcm_uframes_t snd_pcm_pointer(struct snd_pcm_substream *substream);
/* ---------------------------------------- */
/**************************************************************************/
/************************* IMPLEMENTATION *******************************/
/*prototyped*/
static int snd_mychip_free(struct mychip *chip)
{
/* disable hardware here if any */
//.... /* (not implemented in this document) */
/* release the irq */
if (chip->irq >= 0)
free_irq(chip->irq, chip);
/* release the I/O ports & memory */
/*pci_release_regions(chip->pci);*/
clear_mem_regions(chip);
/* disable the PCI entry */
pci_disable_device(chip->pci);
/* release the data */
kfree(chip);
return 0;
}
/*prototyped*/
static int snd_mychip_dev_free(struct snd_device *device)
{
return snd_mychip_free(device->device_data);
}
/*prototyped*/
static void snd_mychip_remove(struct pci_dev *pci)
{
/*Clear the card data and PCI data*/
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
/* constructor -- see "Constructor" sub-section *//*prototyped*/
static int __devinit snd_mychip_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct mychip *chip;
int err;
printk(KERN_ERR "Probing ...\n");
/* (1) Check and increment the device index */
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
/* (2) Create a card instance */
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
if (err < 0)
return err;
/* (3) Create a main component */
/* chip-specific constructor: allocate PCI resources */
err = snd_mychip_create(card, pci, &chip);
if (err < 0) {
snd_card_free(card);
return err;
}
/* (4) Set driver ID and name strings */
strcpy(card->driver, "My Chip");
strcpy(card->shortname, "My Own Chip 123");
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
/* (5) Create other components PCM, mixers, MIDI etc */
/* implemented later */
/* (6) Register card instance */
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
/* (7) Set the PCI driver data */
pci_set_drvdata(pci, card);
dev++;
/*Call PCM constructor*/
err = snd_pcm_constructor(chip);
if(err < 0)
printk(KERN_ERR "Failed to execute PCM constructor!\n");
printk(KERN_ERR "Probing done!\n");
return 0;
}
/*prototyped*/
static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
{
struct mychip *chip = dev_id;
struct timeval timeval;
unsigned int status = 0;
static int executed = 0;
do_gettimeofday(&timeval);
if(executed < 10)
{
/*get the global status register*/
status = ioread32((void*)chip->memregs[IOREG_0].mem_mapped_addr + GLOBAL_STATUS_REGISTER_OFFSET);
status = ioread32((void*)chip->memregs[IOREG_1].mem_mapped_addr + GLOBAL_STATUS_REGISTER_OFFSET);
printk(KERN_ERR "\n---------- ISR -----------\n");
executed ++;
}
return IRQ_HANDLED;
}
/* chip-specific constructor *//*prototyped*/
static int __devinit snd_mychip_create(struct snd_card *card,
struct pci_dev *pci,
struct mychip **rchip)
{
struct mychip *chip;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_mychip_dev_free,
};
printk(KERN_ERR "Enabling PCI device ...");
*rchip = NULL;
/* initialize the PCI entry */
err = pci_enable_device(pci);
if (err < 0)
return err;
/* check PCI availability (28bit DMA) */
if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
printk(KERN_ERR "error to set 28bit mask DMA\n");
pci_disable_device(pci);
return -ENXIO;
}
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL) {
pci_disable_device(pci);
return -ENOMEM;
}
/* initialize the stuff */
chip->card = card;
chip->pci = pci;
chip->irq = -1;
err = reserve_mem_regions(chip);
if (err < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
/* (1) PCI resource allocation */
if (request_irq(pci->irq, snd_mychip_interrupt,
IRQF_SHARED, "My Chip", chip)) {
printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
snd_mychip_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
printk(KERN_ERR "Gain access to PCI IRQ line ... line %d\n", pci->irq);
printk(KERN_ERR "Gain access to PCI IO ports ... ports 0x%x\n", (unsigned int)chip->port);
/* (2) initialization of the chip hardware */
/* (not implemented in this document) */
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
snd_mychip_free(chip);
return err;
}
snd_card_set_dev(card, &pci->dev);
*rchip = chip;
return 0;
}
static void memdump(struct mychip *chip)
{
int i;
for(i=0; i<BARS; i++)
{
printk(KERN_ERR "\n-----------------------------------\n");
printk(KERN_ERR "chip->memregs[i].bar = %d\n", chip->memregs[i].bar);
printk(KERN_ERR "chip->memregs[i].start_addr = 0x%x\n", chip->memregs[i].start_addr);
printk(KERN_ERR "chip->memregs[i].end_addr = 0x%x\n", chip->memregs[i].end_addr);
printk(KERN_ERR "chip->memregs[i].mem_len = %d\n", chip->memregs[i].mem_len);
printk(KERN_ERR "chip->memregs[i].flag = 0x%x\n", chip->memregs[i].flag);
printk(KERN_ERR "chip->memregs[i].mem_mapped_addr = 0x%p\n", chip->memregs[i].mem_mapped_addr);
}
}
static int reserve_mem_regions(struct mychip *chip)
{
int res = 0;
int i=0;
res = pci_request_regions(chip->pci, "My Chip");
if (res < 0) {
return res;
}
for(i=0; i<BARS; i++)
{
chip->memregs[i].bar = i;
chip->memregs[i].start_addr = pci_resource_start( chip->pci, i );
chip->memregs[i].end_addr = pci_resource_end( chip->pci, i );
chip->memregs[i].mem_len = pci_resource_len( chip->pci, i );
chip->memregs[i].flag = pci_resource_flags( chip->pci, i );
chip->memregs[i].mem_mapped_addr = pci_iomap(chip->pci, i, 0);
}
memdump(chip);
return res;
}
static void clear_mem_regions(struct mychip *chip)
{
int i = 0;
for(i=0; i<BARS; i++)
{
chip->memregs[i].bar = 0xFFFF;
chip->memregs[i].start_addr = 0;
chip->memregs[i].end_addr = 0;
chip->memregs[i].mem_len = 0;
chip->memregs[i].flag = 0xFFFF;
pci_iounmap(chip->pci, chip->memregs[i].mem_mapped_addr);
chip->memregs[i].mem_mapped_addr = NULL;
}
pci_release_regions(chip->pci);
}
/* ------------ PCM functions ------------- */
static struct snd_pcm_hardware snd_hardware_setup =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 128 * 1024,
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
/* ---- operator structs ---- */
static struct snd_pcm_ops snd_pcm_playback_ops = {
.open = snd_pcm_playback_open,
.close = snd_pcm_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_pcm_hw_params,
.hw_free = snd_pcm_hw_free,
.prepare = snd_pcm_prepare,
.trigger = snd_pcm_trigger,
.pointer = snd_pcm_pointer,
};
static struct snd_pcm_ops snd_pcm_capture_ops = {
.open = snd_pcm_capture_open,
.close = snd_pcm_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_pcm_hw_params,
.hw_free = snd_pcm_hw_free,
.prepare = snd_pcm_prepare,
.trigger = snd_pcm_trigger,
.pointer = snd_pcm_pointer,
};
/* ---- file operations fcns ---- */
static int snd_pcm_capture_open(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err = -10;
printk(KERN_ERR ">>> snd_pcm_capture_open()\n");
runtime->hw = snd_hardware_setup;
chip->substream = substream;
return err;
};
static int snd_pcm_capture_close(struct snd_pcm_substream *substream)
{
int err = -10;
printk(KERN_ERR ">>> snd_pcm_capture_close()\n");
return err;
};
static int snd_pcm_playback_open(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err = -10;
printk(KERN_ERR ">>> snd_pcm_playback_open()\n");
runtime->hw = snd_hardware_setup;
chip->substream = substream;
return err;
};
static int snd_pcm_playback_close(struct snd_pcm_substream *substream)
{
int err = -10;
printk(KERN_ERR ">>> snd_pcm_playback_close()\n");
return err;
};
/*snd_pcm_lib_ioctl();*/
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
int err = 0;
return err;
};
static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
{
int err = 0;
return err;
};
static int snd_pcm_prepare(struct snd_pcm_substream *substream)
{
int err = 0;
return err;
};
static int snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
int err = 0;
return err;
};
static snd_pcm_uframes_t snd_pcm_pointer(struct snd_pcm_substream *substream)
{
int err = 0;
return err;
};
/* ---- constructor ---- */
static int __devinit snd_pcm_constructor(struct mychip *chip)
{
struct snd_pcm *pcm = NULL;
int err = 0;
printk(KERN_ERR ">>> PCM CONSTRUCTOR: running...\n");
err = snd_pcm_new(chip->card, "My Own Chip", 0, 1, 1, &pcm);
if(err < 0)
{
printk(KERN_ERR ">>> PCM CONSTRUCTOR: Failed to execute snd_pcm_new()!\n");
return err;
}
pcm->private_data = chip;
pcm->private_free = snd_pcm_destructor;
strcpy(pcm->name, "My Own Chip");
chip->pcm = pcm;
/*set operators*/
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pcm_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pcm_capture_ops);
/*stream preallocation of buffers*/
err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
if(err < 0)
{
printk(KERN_ERR ">>> PCM CONSTRUCTOR: Failed to execute snd_pcm_lib_preallocate_pages_for_all()!\n");
return err;
}
printk(KERN_ERR ">>> PCM CONSTRUCTOR: ... exiting.\n");
return 0;
}
/* ---- destructor ---- */
static void snd_pcm_destructor(struct snd_pcm *pcm)
{
/*
struct mychip *chip = snd_pcm_chip(pcm);
*/
}
/* ---------------------------------------- */
/* PCI IDs */
static struct pci_device_id snd_mychip_ids[] = {
{ VENDOR_ID, DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_mychip_ids);
/* pci_driver definition */
static struct pci_driver driver = {
.name = "My Own Chip",
.id_table = snd_mychip_ids,
.probe = snd_mychip_probe,
.remove = __devexit_p(snd_mychip_remove),
};
/**************** MODULE EXCUTED FUNCTIONS ****************************/
/* module initialization */
static int __init alsa_card_mychip_init(void)
{
printk(KERN_ERR "\n\n\n###########################################\n");
printk(KERN_ERR "-->>> Module init: !!!");
return pci_register_driver(&driver);
}
/* module clean up */
static void __exit alsa_card_mychip_exit(void)
{
pci_unregister_driver(&driver);
printk(KERN_ERR "-->>> Module exit: ");
}
module_init(alsa_card_mychip_init)
module_exit(alsa_card_mychip_exit)
And the output I get from dmesg:
[ 227.202006] ###########################################
[ 227.202430] -->>> Module init: !!!
[ 227.202507] Probing ...
[ 227.203959] Enabling PCI device ...
[ 227.207882]
[ 227.207882] -----------------------------------
[ 227.208097] chip->memregs[i].bar = 0
[ 227.208124] chip->memregs[i].start_addr = 0xd100
[ 227.208148] chip->memregs[i].end_addr = 0xd1ff
[ 227.208174] chip->memregs[i].mem_len = 256
[ 227.208198] chip->memregs[i].flag = 0x40101
[ 227.208223] chip->memregs[i].mem_mapped_addr = 0x0001d100
[ 227.208246]
[ 227.208246] -----------------------------------
[ 227.208271] chip->memregs[i].bar = 1
[ 227.208294] chip->memregs[i].start_addr = 0xd200
[ 227.208318] chip->memregs[i].end_addr = 0xd23f
[ 227.208341] chip->memregs[i].mem_len = 64
[ 227.208364] chip->memregs[i].flag = 0x40101
[ 227.208388] chip->memregs[i].mem_mapped_addr = 0x0001d200
[ 227.208410]
[ 227.208410] -----------------------------------
[ 227.208435] chip->memregs[i].bar = 2
.....
[ 227.209080] Gain access to PCI IRQ line ... line 5
[ 227.209104] Gain access to PCI IO ports ... ports 0x0
[ 227.224202] >>> PCM CONSTRUCTOR: running...
[ 227.225378] >>> PCM CONSTRUCTOR: ... exiting.
[ 227.225403] Probing done!
[ 227.295404]
[ 227.295404] ---------- ISR -----------
[ 227.313297]
[ 227.313297] ---------- ISR -----------
[ 227.327248]
[ 227.327248] ---------- ISR -----------
[ 227.344968]
[ 227.344968] ---------- ISR -----------
[ 227.351703]
[ 227.351703] ---------- ISR -----------
[ 227.360189]
[ 227.360189] ---------- ISR -----------
[ 227.371751]
[ 227.371751] ---------- ISR -----------
[ 227.387809]
[ 227.387809] ---------- ISR -----------
[ 227.396767]
[ 227.396767] ---------- ISR -----------
[ 227.413297]
[ 227.413297] ---------- ISR -----------
You see here the
probe function being called(Probing ...)
enabling the PCI device(Enabling PCI device ...)
the memory regions being allocated
the interrupt line (line 5)
the PCM constructors (>>> PCM CONSTRUCTOR: running...)
some dumps from the interrupt handler.
And some lines from my application:
/* Open PCM device for playback. */
rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
printf(
"unable to open pcm device: %s\n",
snd_strerror(rc));
exit(1);
}
printf("Device opened ... rc = %d \n", rc);
Here I try to open my device. If you pay attention to the driver code above the playback_open() function should printk() some message and should return -10. I did this intentionally. But my application shows that the result from snd_pcm_open() is OK(zero I mean). So it looks like my application does not see my driver but tries to use something else.
I will place also all sound the modules being loaded currently:
Module Size Used by
...
alsa 13273 0 //this is my driver
snd_ac97_codec 105592 0
snd_pcm 80357 2 alsa,snd_ac97_codec
snd_page_alloc 14036 1 snd_pcm
ac97_bus 12670 1 snd_ac97_codec
snd_seq_midi 13132 0
snd_rawmidi 25382 1 snd_seq_midi
snd_seq_midi_event 14475 1 snd_seq_midi
snd_seq 51256 2 snd_seq_midi,snd_seq_midi_event
snd_timer 24503 2 snd_pcm,snd_seq
snd_seq_device 14137 3 snd_seq_midi,snd_rawmidi,snd_seq
snd 62027 7 alsa,snd_ac97_codec,snd_pcm,snd_rawmidi,
snd_seq,snd_timer,snd_seq_device
soundcore 14599 1 snd
Here is the output from lspci -v which will show the info for the audio device:
00:05.0 Multimedia audio controller: Intel Corporation 82801AA AC'97 Audio Controller (rev 01)
Subsystem: Intel Corporation Device 0000
Flags: bus master, medium devsel, latency 0, IRQ 5
I/O ports at d100 [size=256]
I/O ports at d200 [size=64]
Kernel driver in use: My Own Chip
Kernel modules: snd-nedelinxalsaxpci, snd-intel8x0
I build my kernel with my driver, because I thought this might help, but it did not(snd-nedelinxalsaxpci is my driver, and snd-intel8x0 is the original module). "My Own Chip" comes from my driver.
I don't know what other info I could give you.
And thanks a lot for the support.
Your problem is that you are calling the PCM constructor after the call to snd_card_register.
Sound card devices are accessible to userspace only if they are registered after they are created.
There is even a comment in your code that tells you about the correct place to do this:
/* (5) Create other components PCM, mixers, MIDI etc */

Resources