Related
When calling:
pTextServices->TxSendMessage( EM_STREAMIN, ( WPARAM ) SF_RTF, ( LPARAM ) &editStream, &lResult )
editSteam.EditStreamInCallback is not being called, but if SF_TEXT is used instead of SF_RTF, then editSteam.EditStreamInCallback gets called.
According to MS docs:
https://learn.microsoft.com/en-us/windows/win32/api/richedit/nc-richedit-editstreamcallback
An error occurs that prevents the rich edit control from transferring
data into or out of itself. Examples are out-of-memory situations,
failure of a system function, or an invalid character in the read
buffer.
In these cases editStream.dwError should contain a different value from zero, but it is zero.
This is the used RTF: "{\rtf1\ansi\pard test \par}" in the code escaped as:
"{\\rtf1\\ansi\\pard test \\par}"
Thank you
Fixed and working fine thanks to my friend Mr. Bruno Cantero (C3)
#include <Richedit.h>
#include <textserv.h>
typedef struct
{
char * szText;
LONG lSize;
LONG lCount;
} RTFTEXTINFO;
typedef HRESULT ( _stdcall * PCREATETEXTSERVICES ) ( IUnknown *, ITextHost *, IUnknown ** );
const IID IID_ITextServices = { 0x8d33f740, 0xcf58, 0x11ce, { 0xa8, 0x9d, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5 } };
class TTextHost : public ITextHost
{
public:
/* IUnknown */
STDMETHOD( QueryInterface )( REFIID, PVOID * ppvObject ) { * ppvObject = NULL; return S_FALSE; };
STDMETHOD_( ULONG, AddRef )( void ) { return 0; };
STDMETHOD_( ULONG, Release )( void ) { return 0; };
/* ITextHost */
HDC TxGetDC( void ) { return NULL; };
INT TxReleaseDC( HDC ) { return 1; };
BOOL TxShowScrollBar( INT, BOOL ) { return FALSE; };
BOOL TxEnableScrollBar( INT, INT ) { return FALSE; };
BOOL TxSetScrollRange( INT, LONG, INT, BOOL ) { return FALSE; };
BOOL TxSetScrollPos( INT, INT, BOOL ) { return FALSE; };
void TxInvalidateRect( LPCRECT, BOOL ) {};
void TxViewChange( BOOL ) {};
BOOL TxCreateCaret( HBITMAP, INT, INT ) { return FALSE; };
BOOL TxShowCaret( BOOL ) { return FALSE; };
BOOL TxSetCaretPos( INT, INT ) { return FALSE; };
BOOL TxSetTimer( UINT, UINT ) { return FALSE; };
void TxKillTimer( UINT ) {};
void TxScrollWindowEx( INT, INT, LPCRECT, LPCRECT, HRGN, LPRECT, UINT ) {};
void TxSetCapture( BOOL ) {};
void TxSetFocus( void ) {};
void TxSetCursor( HCURSOR, BOOL ) {};
BOOL TxScreenToClient( LPPOINT ) { return FALSE; };
BOOL TxClientToScreen( LPPOINT ) { return FALSE; };
HRESULT TxActivate( LONG * ) { return S_OK; };
HRESULT TxDeactivate( LONG ) { return S_OK; };
HRESULT TxGetClientRect( LPRECT prc ) { SetRectEmpty( prc ); return S_OK; };
HRESULT TxGetViewInset( LPRECT prc ) { SetRectEmpty( prc ); return S_OK; };
HRESULT TxGetCharFormat( const CHARFORMATW ** ppCF ) { * ppCF = FCharFormat; return S_OK; };
HRESULT TxGetParaFormat( const PARAFORMAT ** ppPF ) { * ppPF = FParaFormat; return S_OK; };
COLORREF TxGetSysColor( int iIndex ) { return GetSysColor( iIndex ); };
HRESULT TxGetBackStyle( TXTBACKSTYLE * pstyle ) { * pstyle = TXTBACK_TRANSPARENT; return S_OK; };
HRESULT TxGetMaxLength( DWORD * plength ) { * plength = INFINITE; return S_OK; };
HRESULT TxGetScrollBars( DWORD * pdwScrollBar ) { * pdwScrollBar = 0; return S_OK; };
HRESULT TxGetPasswordChar( _Out_ TCHAR * pch ) { return S_FALSE; };
HRESULT TxGetAcceleratorPos( LONG * pcp ) { * pcp = -1; return S_OK; };
HRESULT TxGetExtent( LPSIZEL ) { return E_NOTIMPL; };
HRESULT OnTxCharFormatChange( const CHARFORMATW * pCF ) { _bcopy( FCharFormat, ( void * ) pCF, pCF->cbSize ); return S_OK; };
HRESULT OnTxParaFormatChange( const PARAFORMAT * pPF ) { _bcopy( FParaFormat, ( void * ) pPF, pPF->cbSize ); return S_OK; };
HRESULT TxGetPropertyBits( DWORD, DWORD * pdwBits ) { * pdwBits = TXTBIT_RICHTEXT | TXTBIT_MULTILINE | TXTBIT_WORDWRAP | TXTBIT_USECURRENTBKG; return S_OK; };
HRESULT TxNotify( DWORD, void * ) { return S_OK; };
HIMC TxImmGetContext( void ) { return NULL; };
void TxImmReleaseContext( HIMC ) {};
HRESULT TxGetSelectionBarWidth( LONG * lSelBarWidth ) { * lSelBarWidth = 100; return S_OK; };
CHARFORMATW * FCharFormat;
PARAFORMAT * FParaFormat;
};
static DWORD CALLBACK EditStreamCallback( DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb )
{
RTFTEXTINFO * pRtfTextInfo;
pRtfTextInfo = ( RTFTEXTINFO * ) dwCookie;
if( pRtfTextInfo->lSize - pRtfTextInfo->lCount < cb )
* pcb = pRtfTextInfo->lSize - pRtfTextInfo->lCount;
else
* pcb = cb;
memcpy( pbBuff, pRtfTextInfo->szText, * pcb );
pRtfTextInfo->lCount += * pcb;
return 0;
}
extern "C" void SayRTF( void )
{
LRESULT lResult;
HDC hDC;
HFONT hFont;
HMODULE hDLL;
RECT stRect;
LOGFONT stLogFont;
EDITSTREAM stEditStream;
PCREATETEXTSERVICES pCreateTextServices;
CHARFORMATW stCharFormat;
PARAFORMAT stParaFormat;
RTFTEXTINFO sRtfTextInfo;
IUnknown * pUnknown;
ITextServices * pTextServices;
TTextHost * pTextHost;
/* HDC donde dibujar. */
hDC = ( HDC ) hb_parnl( 1 ); // the hDC where you want to show the RTF
// Comentamos para probar la transperencia Rectangle( hDC, 9, 9, 202, 202 );
stRect = { 10, 10, 200, 200 };
hDLL = LoadLibrary( "Riched20.dll" );
if( hDLL == NULL )
return;
pCreateTextServices = ( PCREATETEXTSERVICES ) GetProcAddress( hDLL, "CreateTextServices" );
if( pCreateTextServices == NULL )
{
FreeLibrary( hDLL );
return;
}
/* Obtenemos las caracterĂsticas de la fuente del HDC. */
hFont = ( HFONT ) SelectObject( hDC, GetStockObject( SYSTEM_FONT ) );
GetObject( hFont, sizeof( LOGFONT ), &stLogFont );
SelectObject( hDC, hFont );
/* Creamos el formato de la fuente por defecto. */
memset( &stCharFormat, 0, sizeof( CHARFORMATW ) );
stCharFormat.cbSize = sizeof( CHARFORMATW );
stCharFormat.dwEffects = CFM_EFFECTS | CFE_AUTOBACKCOLOR;
stCharFormat.dwEffects &= ~( CFE_PROTECTED | CFE_LINK | CFE_AUTOCOLOR );
if( stLogFont.lfWeight < FW_BOLD )
stCharFormat.dwEffects &= ~CFE_BOLD;
if( !stLogFont.lfItalic )
stCharFormat.dwEffects &= ~CFE_ITALIC;
if( !stLogFont.lfUnderline )
stCharFormat.dwEffects &= ~CFE_UNDERLINE;
if( !stLogFont.lfStrikeOut )
stCharFormat.dwEffects &= ~CFE_STRIKEOUT;
stCharFormat.dwMask = CFM_ALL | CFM_BACKCOLOR | CFM_STYLE;
stCharFormat.bCharSet = stLogFont.lfCharSet;
stCharFormat.bPitchAndFamily = stLogFont.lfPitchAndFamily;
stCharFormat.yHeight = -stLogFont.lfHeight * 1440 / GetDeviceCaps( hDC, LOGPIXELSY );
MultiByteToWideChar( CP_ACP, 0, stLogFont.lfFaceName, LF_FACESIZE, stCharFormat.szFaceName, LF_FACESIZE );
/* Creamos el formato de párrafo por defecto. */
memset( &stParaFormat, 0, sizeof( PARAFORMAT ) );
stParaFormat.cbSize = sizeof( PARAFORMAT );
stParaFormat.dwMask = PFM_ALL;
stParaFormat.wAlignment = PFA_LEFT;
stParaFormat.cTabCount = 1;
stParaFormat.rgxTabs[ 0 ] = lDefaultTab;
/* Instanciamos nuestro objeto host. */
pTextHost = new TTextHost;
pTextHost->FCharFormat = &stCharFormat;
pTextHost->FParaFormat = &stParaFormat;
/* Creamos el objeto IUnknown y lo asociamos a nuestro objeto TTextHost. */
if( pCreateTextServices( NULL, pTextHost, &pUnknown ) != S_OK )
{
delete pTextHost;
FreeLibrary( hDLL );
return;
}
/* Obtenemos el interface ITextServices. */
pTextServices = NULL;
pUnknown->QueryInterface( IID_ITextServices, ( void ** ) &pTextServices );
/* Liberamos el objeto IUnknown. */
pUnknown->Release();
if( pTextServices != NULL )
{
// sRtfTextInfo.szText = "Plaint Text";
sRtfTextInfo.szText = "{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fswiss\\fprq2\\fcharset0 Tahoma;}{\\f1\\fswiss\\fcharset0 Arial;}}{\\colortbl ;\\red128\\green0\\blue0;\\red0\\green0\\blue128;\\red0\\green128\\blue0;}\\viewkind4\\uc1\\pard\\f0\\fs20 H\\fs24 E\\b L\\ul\\fs20 L\\i O\\ulnone\\b0\\i0 \\cf1 W\\b\\fs22 O\\cf2\\ul\\b0 R\\i\\fs28 L\\cf3\\ulnone\\b\\i0\\fs20 D\\cf0\\b0\\f1\\par}";
sRtfTextInfo.lSize = lstrlen( sRtfTextInfo.szText );
sRtfTextInfo.lCount = 0;
stEditStream.dwCookie = ( DWORD_PTR ) &sRtfTextInfo;
stEditStream.pfnCallback = EditStreamCallback;
// if( pTextServices->TxSendMessage( EM_STREAMIN, SF_TEXT, ( LPARAM ) &stEditStream, &lResult ) == S_OK )
if( pTextServices->TxSendMessage( EM_STREAMIN, SF_RTF, ( LPARAM ) &stEditStream, &lResult ) == S_OK )
pTextServices->TxDraw( DVASPECT_CONTENT, 0, NULL, NULL, hDC, NULL, ( RECTL * ) &stRect, NULL, NULL, NULL, 0, TXTVIEW_INACTIVE );
/* Liberamos el objeto ITextServices. */
pTextServices->Release();
}
delete pTextHost;
FreeLibrary( hDLL );
}
I'm making a BMP to PNG Converter.
The BMP Images are uncompressed.
So my question is when the BMP File is converted to a PNG File, then is the PNG File compressed i.e is the image data compressed. If so how is the image data aligned in the PNG File Format?
If the PNG Image Data is uncompressed then how is the BMP Image Data Aligned in PNG File.
#include<stdio.h>
#include<stdlib.h>
#pragma pack(1)
typedef struct
{
unsigned short int st_m40_FileType;
unsigned int st_m40_ImgFileSize;
unsigned short int st_m40_RsvdBytes1;
unsigned short int st_m40_RsvdBytes2;
unsigned int st_m40_ImgDataOffset;
unsigned int st_m40_Info40HeaderSze;
int st_m40_PxWdth;
int st_m40_PxHeight;
unsigned short int st_m40_NumOfColrPlnes;
unsigned short int st_m40_BitsPerPixel;
unsigned int st_m40_CmprsnType;
unsigned int st_m40_AftrCmprsnImgDataSze;
int st_m40_xResolutn_ppm;
int st_m40_yResolutn_ppm;
unsigned int st_m40_NumOfColrTbleColrs;
unsigned int st_m40_NumOfImprtntColrs;
}ST_BMP40HEADER_t;
typedef struct
{
ST_BMP40HEADER_t st_mBMP40MainHdrVar;
unsigned char * pst_mImgeData;
}ST_IMAGE_40_HEADER_t;
typedef enum
{
BI_RGB = 0x00,
BI_RLE8,
BI_RLE4,
BI_BITFIELDS,
BI_JPEG,
BI_PNG,
BI_ALPHABITFIELDS,
BI_CMYK,
BI_CMYKRLE8,
BI_CMYKRLE4,
}EN_BMP_COMPRESN_TYPE_t;
#pragma pack(1)
typedef struct
{
int st_mImgWdth;
int st_ImgHeight;
unsigned char st_mBitDepth;
unsigned char st_mColorType;
unsigned char st_mCmprsnMthd;
unsigned char st_mFltrMthd;
unsigned char st_mInterlaceMthd;
}ST_IHDR_DATA_FIELDS_t;
#pragma pack(1)
typedef struct
{
unsigned int st_mChunkDataLngth;
unsigned char st_mChunkType[4];
ST_IHDR_DATA_FIELDS_t st_IHDR_DataFieldsVar;
unsigned int st_mCRC32CheckSum;
}ST_IHDR_t;
#pragma pack(1)
typedef struct
{
unsigned int st_mPLTE_ChunkDataLngth;
unsigned char st_mPLTE_ChunkType[4];
unsigned char* st_mPLTE_ChunkDataPtr;
unsigned int st_mPLTE_CRC32CheckSum;
}ST_PLTE_t;
#pragma pack(1)
typedef struct
{
unsigned int st_mIDAT_ChunkDataLngth;
unsigned char st_mIDAT_ChunkType[4];
unsigned char* st_mIDAT_ChunkDataPtr;
unsigned int st_mIDAT_CRC32CheckSum;
}ST_IDAT_t;
#pragma pack(1)
typedef struct
{
unsigned int st_mIEND_ChunkDataLength;
unsigned char st_mIEND_ChunkType[4];
unsigned char * st_mIEND_ChunkDataPtr;
unsigned int st_mIEND_CRC32CheckSum;
}ST_IEND_t;
/* Little Endian to Big-Endian API. */
unsigned int BMP2PNG_LitToBigEndian(unsigned int f_BigEndianVal)
{
return (((f_BigEndianVal>>24) & 0x000000ff) | ((f_BigEndianVal>>8) & 0x0000ff00) |\
((f_BigEndianVal<<8) & 0x00ff0000) | ((f_BigEndianVal<<24) & 0xff000000));
}
/* CRC32 API.*/
unsigned int BMP2PNG_CmputeCRC32(unsigned char *p_fIpVal, unsigned int f_IpSize)
{
int l_LoopVar;
unsigned int l_ByteVal, l_CRCVal, l_MaskVal;
unsigned int l_TmpIncVar = 0;
l_CRCVal = 0xFFFFFFFF;
while (l_TmpIncVar <= f_IpSize)
{
// Get next byte.
l_ByteVal = p_fIpVal[l_TmpIncVar];
l_CRCVal = l_CRCVal ^ l_ByteVal;
for (l_LoopVar = 7; l_LoopVar >= 0; l_LoopVar--)
{
// Do eight times.
l_MaskVal = -(l_CRCVal & 1);
l_CRCVal = (l_CRCVal >> 1) ^ (0xEDB88320 & l_MaskVal);
}
l_TmpIncVar = l_TmpIncVar + 1;
}
return ~l_CRCVal;
}
/* API converts BMP-to-PNG format. */
char * Convert__40_BMP2PNG(ST_IMAGE_40_HEADER_t * pst_fBMP40ImgHdrVar, FILE * p_ImgFilePtr,\
unsigned char * p_fBMP_ColrTble)
{
FILE * l_BMP2PNGFilePtr;
ST_IHDR_t * pst_lIHDRChunkVar;
unsigned char l_PNGSignature[8]={137, 80, 78, 71, 13, 10, 26, 10};
unsigned int l_BigEndianVal;
unsigned int l_CRC32Val;
unsigned char * l_TempCRCBuff;
unsigned char * l_CRCBuffer;
unsigned int l_BuffLength = 0;
unsigned int l_TempIHDRChunkDataLngth;
pst_lIHDRChunkVar = (ST_IHDR_t *) malloc(sizeof(*pst_lIHDRChunkVar));
l_BMP2PNGFilePtr = fopen("BMP2PNG2.png","w");
/* Writing the 1st 8-bytes to the PNG File.*/
fwrite(l_PNGSignature,1,8,l_BMP2PNGFilePtr);
fseek(l_BMP2PNGFilePtr,8,SEEK_SET);
pst_lIHDRChunkVar->st_mChunkDataLngth = 13;
l_TempIHDRChunkDataLngth = BMP2PNG_LitToBigEndian(13);
/*Writing the IHDR Chunk Data-Length-Size value to the file.*/
fwrite(&l_TempIHDRChunkDataLngth,sizeof(unsigned int),1,l_BMP2PNGFilePtr);
pst_lIHDRChunkVar->st_mChunkType[0] = 'I';
pst_lIHDRChunkVar->st_mChunkType[1] = 'H';
pst_lIHDRChunkVar->st_mChunkType[2] = 'D';
pst_lIHDRChunkVar->st_mChunkType[3] = 'R';
/* Writing the IHDR-Chunk-Type value to the file.*/
fwrite( pst_lIHDRChunkVar->st_mChunkType ,sizeof(unsigned char),4,l_BMP2PNGFilePtr);
// pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mImgWdth = BMP2PNG_LitToBigEndian(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxWdth);
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mImgWdth = BMP2PNG_LitToBigEndian(3);
// pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_ImgHeight = BMP2PNG_LitToBigEndian(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxHeight);
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_ImgHeight = BMP2PNG_LitToBigEndian(3);
if(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel <= 8)
{
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mBitDepth = pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel;
}
else if( (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 24) || \
(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 32) )
{
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mBitDepth = 8;
}
else
{
/*No Action.*/
}
if( pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel <=8 )
{
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mColorType = 3;
}
else if (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 24)
{
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mColorType = 2;
}
else if (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 32)
{
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mColorType = 6;
}
else
{
/*No Action.*/
}
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mCmprsnMthd = 0;
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mFltrMthd = 0;
pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mInterlaceMthd = 0;
fseek(l_BMP2PNGFilePtr,16,SEEK_SET);
fwrite(&pst_lIHDRChunkVar->st_IHDR_DataFieldsVar,sizeof(ST_IHDR_DATA_FIELDS_t),1,l_BMP2PNGFilePtr);
{
/* Framing the data for calculating the 32-bit CRC Checksum.*/
l_BuffLength = 4+(pst_lIHDRChunkVar->st_mChunkDataLngth);
l_CRCBuffer = (unsigned char*)malloc(l_BuffLength);
l_TempCRCBuff = l_CRCBuffer;
*l_TempCRCBuff++ = pst_lIHDRChunkVar->st_mChunkType[0];
*l_TempCRCBuff++ = pst_lIHDRChunkVar->st_mChunkType[1];
*l_TempCRCBuff++ = pst_lIHDRChunkVar->st_mChunkType[2];
*l_TempCRCBuff++ = pst_lIHDRChunkVar->st_mChunkType[3];
*l_TempCRCBuff++ = (unsigned char)pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mImgWdth;
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mImgWdth << 8);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mImgWdth << 16);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mImgWdth << 24);
*l_TempCRCBuff++ = (unsigned char)pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_ImgHeight;
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_ImgHeight << 8);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_ImgHeight << 16);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_ImgHeight << 24);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mBitDepth);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mColorType);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mCmprsnMthd);
*l_TempCRCBuff++ = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mFltrMthd);
*l_TempCRCBuff = (unsigned char)(pst_lIHDRChunkVar->st_IHDR_DataFieldsVar.st_mInterlaceMthd);
// Compute and output CRC
l_CRC32Val = BMP2PNG_CmputeCRC32(l_CRCBuffer, l_BuffLength);
l_CRC32Val = BMP2PNG_LitToBigEndian( l_CRC32Val);
fseek(l_BMP2PNGFilePtr,29,SEEK_SET);
unsigned int returnVal = fwrite(&l_CRC32Val,sizeof(unsigned int),1,l_BMP2PNGFilePtr);
}
unsigned char * p_lBMPImgData = pst_fBMP40ImgHdrVar->pst_mImgeData;
// unsigned int l_BMPImgeDataSze = ( (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxWdth) * \
(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxHeight) );
unsigned int l_BMPImgeDataSze = (3*3)*3;
ST_IDAT_t * pst_lIDAT_ChunkVar;
pst_lIDAT_ChunkVar = (ST_IDAT_t *) malloc(sizeof(*pst_lIDAT_ChunkVar));
unsigned int l_ImgDataSzeIncVar = 0;
unsigned char * p_IDATBuf;
unsigned char * p_TempIDATBuf;
unsigned int l_IDATBufSze;
if(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 24)
{
fseek(l_BMP2PNGFilePtr,33,SEEK_SET);
l_BMPImgeDataSze *= 3;
l_IDATBufSze = 4 + l_BMPImgeDataSze;
p_IDATBuf = (unsigned char*)malloc(l_IDATBufSze);
p_TempIDATBuf = p_IDATBuf;
pst_lIDAT_ChunkVar->st_mIDAT_ChunkDataLngth = BMP2PNG_LitToBigEndian(l_BMPImgeDataSze);
fwrite(&pst_lIDAT_ChunkVar->st_mIDAT_ChunkDataLngth,sizeof(unsigned int),1,l_BMP2PNGFilePtr);
pst_lIDAT_ChunkVar->st_mIDAT_ChunkType[0] = 'I';
pst_lIDAT_ChunkVar->st_mIDAT_ChunkType[1] = 'D';
pst_lIDAT_ChunkVar->st_mIDAT_ChunkType[2] = 'A';
pst_lIDAT_ChunkVar->st_mIDAT_ChunkType[3] = 'T';
fwrite( pst_lIDAT_ChunkVar->st_mIDAT_ChunkType ,sizeof(unsigned char),4,l_BMP2PNGFilePtr);
fseek(l_BMP2PNGFilePtr,41,SEEK_SET);
unsigned char ImgeData[27] = { 0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,0,255,0,};
while(l_ImgDataSzeIncVar <= l_BMPImgeDataSze )
{
fwrite(ImgeData,sizeof(unsigned char),1,l_BMP2PNGFilePtr);
/*
fwrite( (p_lBMPImgData+2),sizeof(unsigned char),1,l_BMP2PNGFilePtr);
*p_TempIDATBuf++ = *(p_lBMPImgData+2);
fwrite( (p_lBMPImgData+1),sizeof(unsigned char),1,l_BMP2PNGFilePtr);
*p_TempIDATBuf++ = *(p_lBMPImgData+1);
fwrite( (p_lBMPImgData),sizeof(unsigned char),1,l_BMP2PNGFilePtr);
*p_TempIDATBuf++ = *(p_lBMPImgData);
p_lBMPImgData += 3;
l_ImgDataSzeIncVar += 3;
*/
}
// Compute and output CRC
unsigned int crc32 = BMP2PNG_CmputeCRC32(p_IDATBuf, l_IDATBufSze);
fwrite(&crc32,sizeof(unsigned int),1,l_BMP2PNGFilePtr);
}
}
ST_IMAGE_40_HEADER_t * ReadBMP40HdrImage(ST_IMAGE_40_HEADER_t * pst_fBMP40ImgHdrVar, \
FILE * p_ImgFilePtr)
{
unsigned short int l_ColorTbleSze;
unsigned char * l_ColorTbleVal;
unsigned int l_ImageDataOffset;
unsigned int l_ImageDataSize;
unsigned int l_ImageDataSzeIncVar = 1;
unsigned char * l_ImageDataPtr;
unsigned int l_PixelCount = 1;
int l_NumOfReadBlks = fread(pst_fBMP40ImgHdrVar,sizeof(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar),1,p_ImgFilePtr);
printf("\n********************************************************************\n");
printf("\nPrinting the BMP Image Parameters:\n");
printf("\nBMP File Header Parameters.\n");
printf("File Type:\t'BM'\n");
printf("Total Size of the image:\t%u\n",pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_ImgFileSize);
printf("Image Data Offset:\t%u\n",pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_ImgDataOffset);
printf("\nBMP Info Header Parameters.\n");
printf("Size of the Info-Header:\t'%u-Bytes'.\n",pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_Info40HeaderSze);
printf("Width of the BMP Image:\t'%d-Pixels'.\n",pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxWdth);
printf("Height of the BMP Image:\t'%d-Pixels'.\n",pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxHeight);
printf("Number of Color Planes:\t'%d'.\n",pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_NumOfColrPlnes);
printf("Number of Bits-Per-Pixel:\t'%d'.\n",pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel);
printf("Compression Type used for compressing the BMP Raw Image:\t'%d'.\n",\
pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_CmprsnType);
printf("Horizontal Resolution:\t'%d-Pixels Per Meter'.\n,",\
pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_xResolutn_ppm);
printf("Vertical Resolution:\t'%d-Pixels Per Meter'.\n,",\
pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_yResolutn_ppm);
if(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_NumOfColrTbleColrs !=0 )
{
printf("Number of Colors in the color table:\t'%u'.\n",\
pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_NumOfColrTbleColrs);
}
if( (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel <= 8) && \
(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_NumOfColrTbleColrs !=0) )
{
rewind(p_ImgFilePtr);
fseek(p_ImgFilePtr,55,SEEK_SET);
l_ColorTbleSze = (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_NumOfColrTbleColrs) * 4 ;
l_ColorTbleVal = (unsigned char*) malloc( l_ColorTbleSze );
l_NumOfReadBlks = fread(l_ColorTbleVal, l_ColorTbleSze, 1, p_ImgFilePtr);
if(l_NumOfReadBlks <1)
{
printf("Couldn't read the Color-Table values from the BMP File.\n");
return NULL;
}
else if(l_NumOfReadBlks == 1)
{
unsigned short int l_ClrTbleIncVar = 0;
unsigned char * l_TempColorTbleVal = l_ColorTbleVal;
unsigned short int l_IndexVal = 0;
printf("\n*****************************************************************************\n");
printf("Printing the color Table.\n");
while (l_ClrTbleIncVar <= (l_ColorTbleSze-4) )
{
printf("Index '%d' : ", l_IndexVal);
printf("Blue -> '%d'\t",*l_TempColorTbleVal);
l_TempColorTbleVal++;
printf("Green -> '%d'\t",*l_TempColorTbleVal);
l_TempColorTbleVal++;
printf("Red -> '%d'\t\n",*l_TempColorTbleVal);
l_TempColorTbleVal+=2;
l_ClrTbleIncVar += 4;
l_IndexVal++;
}
}
}
rewind(p_ImgFilePtr);
l_ImageDataOffset = pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_ImgDataOffset;
fseek(p_ImgFilePtr,l_ImageDataOffset,SEEK_SET);
if(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 4)
{
l_ImageDataSize = ( (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxWdth) * \
(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxHeight) )/2;
pst_fBMP40ImgHdrVar->pst_mImgeData = (unsigned char*)malloc(l_ImageDataSize);
l_ImageDataPtr = pst_fBMP40ImgHdrVar->pst_mImgeData;
l_NumOfReadBlks = fread(l_ImageDataPtr,1,l_ImageDataSize,p_ImgFilePtr);
if(l_NumOfReadBlks < 1)
{
printf("Couldn't read the image data from the file.\n");
return NULL;
}
}
else if(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 8)
{
l_ImageDataSize = ( (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxWdth) * \
(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxHeight) );
pst_fBMP40ImgHdrVar->pst_mImgeData = (unsigned char*)malloc(l_ImageDataSize);
l_ImageDataPtr = pst_fBMP40ImgHdrVar->pst_mImgeData;
l_NumOfReadBlks = fread(l_ImageDataPtr,l_ImageDataSize,1,p_ImgFilePtr);
if(l_NumOfReadBlks < 1)
{
printf("Couldn't read the image data from the file.\n");
return NULL;
}
}
else if(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 24)
{
l_ImageDataSize = ( (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxWdth) * \
(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxHeight) * 3 );
pst_fBMP40ImgHdrVar->pst_mImgeData = (unsigned char*)malloc(l_ImageDataSize);
l_ImageDataPtr = pst_fBMP40ImgHdrVar->pst_mImgeData;
l_NumOfReadBlks = fread(l_ImageDataPtr,l_ImageDataSize,1,p_ImgFilePtr);
if(l_NumOfReadBlks < 1)
{
printf("Couldn't read the image data from the file.\n");
return NULL;
}
}
else if(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_BitsPerPixel == 32)
{
l_ImageDataSize = ( (pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxWdth) * \
(pst_fBMP40ImgHdrVar->st_mBMP40MainHdrVar.st_m40_PxHeight) * 4 );
pst_fBMP40ImgHdrVar->pst_mImgeData = (unsigned char*)malloc(l_ImageDataSize);
l_ImageDataPtr = pst_fBMP40ImgHdrVar->pst_mImgeData;
l_NumOfReadBlks = fread(l_ImageDataPtr,l_ImageDataSize,1,p_ImgFilePtr);
if(l_NumOfReadBlks < 1)
{
printf("Couldn't read the image data from the file.\n");
return NULL;
}
}
rewind(p_ImgFilePtr);
Convert__40_BMP2PNG(pst_fBMP40ImgHdrVar,p_ImgFilePtr,l_ColorTbleVal);
}
int main()
{
FILE * l_ImgeFilePtr;
l_ImgeFilePtr = fopen("FLAG_B24.BMP","r");
unsigned short int l_ImgeFileType;
unsigned int l_InfoHdrSze;
int l_NumofReadBlocks;
l_NumofReadBlocks = fread(&l_ImgeFileType,2,1,l_ImgeFilePtr);
if(l_NumofReadBlocks <1)
{
printf("Couldn't read the bytes from the file.\n");
return 0;
}
if(l_ImgeFileType != 19778)
{
printf("Image Passed is not a BMP file.\n");
return 0;
}
fseek(l_ImgeFilePtr,14,SEEK_SET);
l_NumofReadBlocks = fread(&l_InfoHdrSze, 4, 1, l_ImgeFilePtr);
if(l_NumofReadBlocks <1)
{
printf("Couldn't read the bytes from the file.\n");
return 0;
}
rewind(l_ImgeFilePtr);
if( l_InfoHdrSze == 40 )
{
ST_IMAGE_40_HEADER_t * pst_Img40HdrVar;
pst_Img40HdrVar = malloc(sizeof(*pst_Img40HdrVar));
if(pst_Img40HdrVar == NULL)
{
printf("There isn't enough memory to allocate for the Image header variable.\n");
return 0;
}
ST_IMAGE_40_HEADER_t * pst_l40_ReadAPIReturnVal = ReadBMP40HdrImage(pst_Img40HdrVar, l_ImgeFilePtr);
}
}
is the PNG File compressed
Yes.
how is the image data aligned in the PNG File Format?
It's not aligned. From the spec, "The chunk data length may be any number of bytes up to the maximum; therefore, implementors cannot assume that chunks are aligned on any boundaries larger than bytes."
Multitudinous image handling libraries exist to save you from worrying about these details, though. I'd recommend against rolling your own converter unless your goal is specifically to learn about image file formats.
I am trying to run following code, which was copied from here. I have made few changes to run it with older kernel versions.
When I insert kernel module, nlmsg_multicast() fails and logs as nlmsg_multicast() error: -3 in /var/log/messages.
While running user space program, socket() fails.
What exactly I want to do is,
kernel module creates a socket, regardless of any process in user space
kernel module send some events to user space
If any process in user space reply to an event, kernel module process on that reply
Since, It may happen that no process in user space available to reply on event, even in that case module must send event and wait for a while for response.
Is it possible to send first message from kernel module to a process in user space? How can I do this?
Kernel module code:
Makefile
obj-m := foo.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
foo.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYGRP 21
static struct sock *nl_sk = NULL;
static void send_to_user(void)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
char *msg = "Hello from kernel";
int msg_size = strlen(msg) + 1;
int res;
pr_info("Creating skb.\n");
skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_KERNEL);
if (!skb) {
pr_err("Allocation failure.\n");
return;
}
nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
strcpy(nlmsg_data(nlh), msg);
pr_info("Sending skb.\n");
res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_KERNEL);
if (res < 0)
pr_info("nlmsg_multicast() error: %d\n", res);
else
pr_info("Success.\n");
}
static int __init hello_init(void)
{
pr_info("Inserting hello module.\n");
//nl_sk = netlink_kernel_create(&init_net, MYPROTO, NULL);
nl_sk = netlink_kernel_create(&init_net, MYPROTO, 0, NULL, NULL, THIS_MODULE);
if (!nl_sk) {
pr_err("Error creating socket.\n");
return -10;
}
send_to_user();
netlink_kernel_release(nl_sk);
return 0;
}
static void __exit hello_exit(void)
{
pr_info("Exiting hello module.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
The user space program:
(Compiled with gcc somename.c)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYMGRP 21
int open_netlink(void)
{
int sock;
struct sockaddr_nl addr;
int group = MYMGRP;
sock = socket(AF_NETLINK, SOCK_RAW, MYPROTO);
if (sock < 0) {
printf("sock < 0.\n");
return sock;
}
memset((void *) &addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
/* This doesn't work for some reason. See the setsockopt() below. */
addr.nl_groups = MYMGRP;
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
printf("bind < 0.\n");
return -1;
}
/*
* 270 is SOL_NETLINK. See
* http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.1#L314
* and
* https://stackoverflow.com/questions/17732044/
*/
/*if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
printf("setsockopt < 0\n");
return -1;
}*/
return sock;
}
void read_event(int sock)
{
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;
char buffer[65536];
int ret;
iov.iov_base = (void *) buffer;
iov.iov_len = sizeof(buffer);
msg.msg_name = (void *) &(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf("Ok, listening.\n");
ret = recvmsg(sock, &msg, 0);
if (ret < 0)
printf("ret < 0.\n");
else
printf("Received message payload: %s\n", NLMSG_DATA((struct nlmsghdr *) &buffer));
}
int main(int argc, char *argv[])
{
int nls;
nls = open_netlink();
if (nls < 0)
return nls;
while (1)
read_event(nls);
return 0;
}
Thank you for your time!
This looks like bad design (because upper layers should depend on lower layers, not the other way around). But if you're convinced the kernel cannot sit idle or operate using default configuration until userspace can fetch info, then first install this tool (might want to read the core guide too), and then do something like this:
Kernel:
#include <linux/module.h>
#include <linux/kernel.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
#define MYPROTO NETLINK_USERSOCK
#define MYGRP 22
static struct sock *nl_sk;
static struct timer_list timer;
void try_send(unsigned long data)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
char *msg = "Hello from kernel";
int msg_size = strlen(msg) + 1;
int res;
skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_ATOMIC);
if (!skb) {
pr_err("Allocation failure.\n");
return;
}
nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
strcpy(nlmsg_data(nlh), msg);
pr_info("Sending multicast.\n");
res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_ATOMIC);
if (res < 0) {
pr_info("nlmsg_multicast() error: %d. Will try again later.\n", res);
/* Wait 1 second. */
mod_timer(&timer, jiffies + msecs_to_jiffies(1000));
} else {
pr_info("Success.\n");
}
}
static int handle_netlink_message(struct sk_buff *skb_in, struct nlmsghdr *nl_hdr)
{
char *hello;
hello = NLMSG_DATA(nl_hdr);
pr_info("Userspace says '%s.'\n", hello);
return 0;
}
static void receive_answer(struct sk_buff *skb)
{
netlink_rcv_skb(skb, &handle_netlink_message);
}
static int __init hello_init(void)
{
pr_info("Inserting module.\n");
nl_sk = netlink_kernel_create(&init_net, MYPROTO, 0, receive_answer, NULL, THIS_MODULE);
if (!nl_sk) {
pr_err("Error creating socket.\n");
return -10;
}
init_timer(&timer);
timer.function = try_send;
timer.expires = jiffies + 1000;
timer.data = 0;
add_timer(&timer);
return 0;
}
static void __exit hello_exit(void)
{
del_timer_sync(&timer);
netlink_kernel_release(nl_sk);
pr_info("Exiting module.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
User (I'm compiling using gcc usr.c -I/usr/include/libnl3 -lnl-3 -Wall, your mileage may vary):
#include <netlink/netlink.h>
#include <netlink/msg.h>
#define MYPROTO NETLINK_USERSOCK
#define MYMGRP 22
struct nl_sock *sk;
void respond_to_kernel(void)
{
char *response = "foo bar";
int error;
error = nl_send_simple(sk, 12345, NLMSG_DONE, response, strlen(response) + 1);
if (error < 0) {
printf("nl_send_simple() threw errcode %d.\n", error);
printf("libnl's message: %s", nl_geterror(error));
} else {
printf("Responded %d bytes.\n", error);
}
}
int receive_kernel_request(struct nl_msg *msg, void *arg)
{
char *hello;
hello = nlmsg_data(nlmsg_hdr(msg));
printf("Kernel says '%s'.\n", hello);
respond_to_kernel();
return 0;
}
int prepare_socket(void)
{
int error;
sk = nl_socket_alloc();
if (!sk) {
printf("nl_socket_alloc() returned NULL.\n");
return -1;
}
nl_socket_disable_seq_check(sk);
error = nl_socket_modify_cb(sk, NL_CB_FINISH, NL_CB_CUSTOM, receive_kernel_request, NULL);
if (error < 0) {
printf("Could not register callback function. Errcode: %d\n", error);
goto fail;
}
error = nl_connect(sk, MYPROTO);
if (error < 0) {
printf("Connection failed: %d\n", error);
goto fail;
}
error = nl_socket_add_memberships(sk, MYMGRP, 0);
if (error) {
printf("Could not register to the multicast group. %d\n", error);
goto fail;
}
return 0;
fail:
printf("libnl's message: %s\n", nl_geterror(error));
nl_socket_free(sk);
return error;
}
int wait_for_kernel_message(void)
{
int error;
printf("Waiting for kernel request...\n");
error = nl_recvmsgs_default(sk);
if (error < 0) {
printf("nl_send_simple() threw errcode %d.\n", error);
printf("libnl's message: %s\n", nl_geterror(error));
return error;
}
return 0;
}
void destroy_socket(void)
{
nl_socket_free(sk);
}
int main(int argc, char *argv[])
{
int error;
error = prepare_socket();
if (error)
return error;
error = wait_for_kernel_message();
destroy_socket();
return error;
}
Tested on kernel 3.2. (Sorry; that's the lowest I have right now.)
This is an example without libnl.
I put all functions in a one file. The coding style is not good. It is only for an example.
I hope it's helpful for you.
I have tested the code in Ubuntu 15.04 which's kernel is kernel 3.19.0-15.
Kernel Module
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/genetlink.h>
static struct timer_list timer;
/* Code based on http://stackoverflow.com/questions/26265453/netlink-multicast-kernel-group/33578010#33578010 */
/**
* This callback runs whenever the socket receives messages.
* We don't use it now, but Linux complains if we don't define it.
*/
static int hello(struct sk_buff *skb, struct genl_info *info)
{
pr_info("Received a message in kernelspace.\n");
return 0;
}
/**
* Attributes are fields of data your messages will contain.
* The designers of Netlink really want you to use these instead of just dumping
* data to the packet payload... and I have really mixed feelings about it.
*/
enum attributes {
/*
* The first one has to be a throwaway empty attribute; I don't know
* why.
* If you remove it, ATTR_HELLO (the first one) stops working, because
* it then becomes the throwaway.
*/
ATTR_DUMMY,
ATTR_HELLO,
ATTR_FOO,
/* This must be last! */
__ATTR_MAX,
};
/**
* Here you can define some constraints for the attributes so Linux will
* validate them for you.
*/
static struct nla_policy policies[] = {
[ATTR_HELLO] = { .type = NLA_STRING, },
[ATTR_FOO] = { .type = NLA_U32, },
};
/**
* Message type codes. All you need is a hello sorta function, so that's what
* I'm defining.
*/
enum commands {
COMMAND_HELLO,
/* This must be last! */
__COMMAND_MAX,
};
/**
* Actual message type definition.
*/
struct genl_ops ops[] = {
{
.cmd = COMMAND_HELLO,
.flags = 0,
.policy = policies,
.doit = hello,
.dumpit = NULL,
},
};
/**
* A Generic Netlink family is a group of listeners who can and want to speak
* your language.
* Anyone who wants to hear your messages needs to register to the same family
* as you.
*/
struct genl_family family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = "PotatoFamily",
.version = 1,
.maxattr = __ATTR_MAX,
};
/**
* And more specifically, anyone who wants to hear messages you throw at
* specific multicast groups need to register themselves to the same multicast
* group, too.
*/
struct genl_multicast_group groups[] = {
{ .name = "PotatoGroup" },
};
void send_multicast(unsigned long arg)
{
struct sk_buff *skb;
void *msg_head;
unsigned char *msg = "TEST";
int error;
pr_info("----- Running timer -----\n");
pr_info("Newing message.\n");
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
pr_err("genlmsg_new() failed.\n");
goto end;
}
pr_info("Putting message.\n");
msg_head = genlmsg_put(skb, 0, 0, &family, 0, COMMAND_HELLO);
if (!msg_head) {
pr_err("genlmsg_put() failed.\n");
kfree_skb(skb);
goto end;
}
pr_info("Nla_putting string.\n");
error = nla_put_string(skb, ATTR_HELLO, msg);
if (error) {
pr_err("nla_put_string() failed: %d\n", error);
kfree_skb(skb);
goto end;
}
pr_info("Nla_putting integer.\n");
error = nla_put_u32(skb, ATTR_FOO, 12345);
if (error) {
pr_err("nla_put_u32() failed: %d\n", error);
kfree_skb(skb);
goto end;
}
pr_info("Ending message.\n");
genlmsg_end(skb, msg_head);
pr_info("Multicasting message.\n");
/*
* The family has only one group, so the group ID is just the family's
* group offset.
* mcgrp_offset is supposed to be private, so use this value for debug
* purposes only.
*/
pr_info("The group ID is %u.\n", family.mcgrp_offset);
error = genlmsg_multicast_allns(&family, skb, 0, 0, GFP_KERNEL);
if (error) {
pr_err("genlmsg_multicast_allns() failed: %d\n", error);
pr_err("(This can happen if nobody is listening. "
"Because it's not that unexpected, "
"you might want to just ignore this error.)\n");
goto end;
}
pr_info("Success.\n");
end:
mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}
static int init_socket(void)
{
int error;
pr_info("Registering family.\n");
error = genl_register_family_with_ops_groups(&family, ops, groups);
if (error)
pr_err("Family registration failed: %d\n", error);
return error;
}
static void initialize_timer(void)
{
pr_info("Starting timer.\n");
init_timer(&timer);
timer.function = send_multicast;
timer.expires = 0;
timer.data = 0;
mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}
static int __init hello_init(void)
{
int error;
error = init_socket();
if (error)
return error;
initialize_timer();
pr_info("Hello module registered.\n");
return 0;
}
static void __exit hello_exit(void)
{
del_timer_sync(&timer);
genl_unregister_family(&family);
pr_info("Hello removed.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Kernel Module Makefile
PWD := $(shell pwd)
KVERSION := $(shell uname -r)
KERNEL_DIR = /usr/src/linux-headers-$(KVERSION)/
MODULE_NAME = genl_kern_grp
obj-m := $(MODULE_NAME).o
all:
make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
make -C $(KERNEL_DIR) M=$(PWD) clean
User Code - main.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <linux/genetlink.h>
/* Code based on libnl-3 */
/* Code based on http://stackoverflow.com/questions/26265453/netlink-multicast-kernel-group/33578010#33578010 */
/* Code based on http://www.electronicsfaq.com/2014/02/generic-netlink-sockets-example-code.html */
/* Code based on http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html */
/* Based on libnl-3 attr.h */
/**
* #ingroup attr
* Basic attribute data types
*
* See section #core_doc{core_attr_parse,Attribute Parsing} for more details.
*/
enum {
NLA_UNSPEC, /**< Unspecified type, binary data chunk */
NLA_U8, /**< 8 bit integer */
NLA_U16, /**< 16 bit integer */
NLA_U32, /**< 32 bit integer */
NLA_U64, /**< 64 bit integer */
NLA_STRING, /**< NUL terminated character string */
NLA_FLAG, /**< Flag */
NLA_MSECS, /**< Micro seconds (64bit) */
NLA_NESTED, /**< Nested attributes */
NLA_NESTED_COMPAT,
NLA_NUL_STRING,
NLA_BINARY,
NLA_S8,
NLA_S16,
NLA_S32,
NLA_S64,
__NLA_TYPE_MAX,
};
#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
/**
* #ingroup attr
* Attribute validation policy.
*
* See section #core_doc{core_attr_parse,Attribute Parsing} for more details.
*/
struct nla_policy {
/** Type of attribute or NLA_UNSPEC */
uint16_t type;
/** Minimal length of payload required */
uint16_t minlen;
/** Maximal length of payload allowed */
uint16_t maxlen;
};
/**
* Attributes and commands have to be the same as in kernelspace, so you might
* want to move these enums to a .h and just #include that from both files.
*/
enum attributes {
ATTR_DUMMY,
ATTR_HELLO,
ATTR_FOO,
/* This must be last! */
__ATTR_MAX,
};
enum commands {
COMMAND_HELLO,
/* This must be last! */
__COMMAND_MAX,
};
/* Generic macros for dealing with netlink sockets. Might be duplicated
* elsewhere. It is recommended that commercial grade applications use
* libnl or libnetlink and use the interfaces provided by the library
*/
#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
/* Family string */
#define GEN_FAMILY_STR "PotatoFamily"
#define GEN_ML_GRP_STR "PotatoGroup"
/* SOL_NETLINK is only defined in <kernel src>/include/linux/socket.h
* It is not defined in <kernel src>/include/uapi/linux/socket.h
* Thus, copy the define to here if we don't include kernel header
*/
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
/**
* #ingroup attr
* Iterate over a stream of attributes
* #arg pos loop counter, set to current attribute
* #arg head head of attribute stream
* #arg len length of attribute stream
* #arg rem initialized to len, holds bytes currently remaining in stream
*/
#define nla_for_each_attr(pos, head, len, rem) \
for (pos = head, rem = len; \
nla_ok(pos, rem); \
pos = nla_next(pos, &(rem)))
/**
* #ingroup attr
* Iterate over a stream of nested attributes
* #arg pos loop counter, set to current attribute
* #arg nla attribute containing the nested attributes
* #arg rem initialized to len, holds bytes currently remaining in stream
*/
#define nla_for_each_nested(pos, nla, rem) \
for (pos = nla_data(nla), rem = nla_len(nla); \
nla_ok(pos, rem); \
pos = nla_next(pos, &(rem)))
/* Variables used for netlink */
int nl_fd; /* netlink socket's file descriptor */
struct sockaddr_nl nl_address; /* netlink socket address */
int nl_family_id; /* The family ID resolved by the netlink controller for this userspace program */
int nl_rxtx_length; /* Number of bytes sent or received via send() or recv() */
struct nlattr *nl_na; /* pointer to netlink attributes structure within the payload */
struct { /* memory for netlink request and response messages - headers are included */
struct nlmsghdr n;
struct genlmsghdr g;
char buf[256];
} nl_request_msg, nl_response_msg;
/* Base on libnl-3 attr.c */
/**
* Return type of the attribute.
* #arg nla Attribute.
*
* #return Type of attribute.
*/
int nla_type(const struct nlattr *nla)
{
return nla->nla_type & NLA_TYPE_MASK;
}
/**
* Return pointer to the payload section.
* #arg nla Attribute.
*
* #return Pointer to start of payload section.
*/
void *nla_data(const struct nlattr *nla)
{
return (char *) nla + NLA_HDRLEN;
}
/**
* Return length of the payload .
* #arg nla Attribute
*
* #return Length of payload in bytes.
*/
int nla_len(const struct nlattr *nla)
{
return nla->nla_len - NLA_HDRLEN;
}
/**
* Check if the attribute header and payload can be accessed safely.
* #arg nla Attribute of any kind.
* #arg remaining Number of bytes remaining in attribute stream.
*
* Verifies that the header and payload do not exceed the number of
* bytes left in the attribute stream. This function must be called
* before access the attribute header or payload when iterating over
* the attribute stream using nla_next().
*
* #return True if the attribute can be accessed safely, false otherwise.
*/
int nla_ok(const struct nlattr *nla, int remaining)
{
return remaining >= sizeof(*nla) &&
nla->nla_len >= sizeof(*nla) &&
nla->nla_len <= remaining;
}
/**
* Return next attribute in a stream of attributes.
* #arg nla Attribute of any kind.
* #arg remaining Variable to count remaining bytes in stream.
*
* Calculates the offset to the next attribute based on the attribute
* given. The attribute provided is assumed to be accessible, the
* caller is responsible to use nla_ok() beforehand. The offset (length
* of specified attribute including padding) is then subtracted from
* the remaining bytes variable and a pointer to the next attribute is
* returned.
*
* nla_next() can be called as long as remainig is >0.
*
* #return Pointer to next attribute.
*/
struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
{
int totlen = NLA_ALIGN(nla->nla_len);
*remaining -= totlen;
return (struct nlattr *) ((char *) nla + totlen);
}
static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
[NLA_U8] = sizeof(uint8_t),
[NLA_U16] = sizeof(uint16_t),
[NLA_U32] = sizeof(uint32_t),
[NLA_U64] = sizeof(uint64_t),
[NLA_STRING] = 1,
[NLA_FLAG] = 0,
};
static int validate_nla(const struct nlattr *nla, int maxtype,
const struct nla_policy *policy)
{
const struct nla_policy *pt;
unsigned int minlen = 0;
int type = nla_type(nla);
if (type < 0 || type > maxtype)
return 0;
pt = &policy[type];
if (pt->type > NLA_TYPE_MAX)
return -1;
if (pt->minlen)
minlen = pt->minlen;
else if (pt->type != NLA_UNSPEC)
minlen = nla_attr_minlen[pt->type];
if (nla_len(nla) < minlen)
return -2;
if (pt->maxlen && nla_len(nla) > pt->maxlen)
return -3;
if (pt->type == NLA_STRING) {
const char *data = nla_data(nla);
if (data[nla_len(nla) - 1] != '\0')
return -4;
}
return 0;
}
/**
* Create attribute index based on a stream of attributes.
* #arg tb Index array to be filled (maxtype+1 elements).
* #arg maxtype Maximum attribute type expected and accepted.
* #arg head Head of attribute stream.
* #arg len Length of attribute stream.
* #arg policy Attribute validation policy.
*
* Iterates over the stream of attributes and stores a pointer to each
* attribute in the index array using the attribute type as index to
* the array. Attribute with a type greater than the maximum type
* specified will be silently ignored in order to maintain backwards
* compatibility. If \a policy is not NULL, the attribute will be
* validated using the specified policy.
*
* #see nla_validate
* #return 0 on success or a negative error code.
*/
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
struct nla_policy *policy)
{
struct nlattr *nla;
int rem, err;
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
nla_for_each_attr(nla, head, len, rem) {
int type = nla_type(nla);
if (type > maxtype)
continue;
if (policy) {
err = validate_nla(nla, maxtype, policy);
if (err < 0)
goto errout;
}
if (tb[type])
fprintf(stderr, "Attribute of type %#x found multiple times in message, "
"previous attribute is being ignored.\n", type);
tb[type] = nla;
}
if (rem > 0)
fprintf(stderr, "netlink: %d bytes leftover after parsing "
"attributes.\n", rem);
err = 0;
errout:
return err;
}
/**
* Create attribute index based on nested attribute
* #arg tb Index array to be filled (maxtype+1 elements).
* #arg maxtype Maximum attribute type expected and accepted.
* #arg nla Nested Attribute.
* #arg policy Attribute validation policy.
*
* Feeds the stream of attributes nested into the specified attribute
* to nla_parse().
*
* #see nla_parse
* #return 0 on success or a negative error code.
*/
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
struct nla_policy *policy)
{
return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
}
static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING,
.maxlen = GENL_NAMSIZ },
[CTRL_ATTR_VERSION] = { .type = NLA_U32 },
[CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 },
[CTRL_ATTR_MAXATTR] = { .type = NLA_U32 },
[CTRL_ATTR_OPS] = { .type = NLA_NESTED },
[CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
};
static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = {
[CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING },
[CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 },
};
int genlctrl_msg_parse(struct nlmsghdr *nlh, int *family_id, char **family_name,
int *mcast_id, char **mcast_name)
{
struct nlattr *tb[CTRL_ATTR_MAX+1];
struct nlattr *nla_hdr;
int nla_length;
int ret = 0;
nla_hdr = (struct nlattr *)((unsigned char *) nlh + NLMSG_HDRLEN + GENL_HDRLEN);
nla_length = nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;
if(ret = nla_parse(tb, CTRL_ATTR_MAX, nla_hdr, nla_length, ctrl_policy)) {
fprintf(stderr, "nla_parse error! ret = %d\n", ret);
return -1;
}
if (tb[CTRL_ATTR_FAMILY_ID])
*family_id = *(const uint16_t *) nla_data(tb[CTRL_ATTR_FAMILY_ID]);
if (tb[CTRL_ATTR_FAMILY_NAME])
*family_name = (char *) nla_data(tb[CTRL_ATTR_FAMILY_NAME]);
if (tb[CTRL_ATTR_MCAST_GROUPS]) {
struct nlattr *nla, *grp_attr;
int remaining, err;
grp_attr = tb[CTRL_ATTR_MCAST_GROUPS];
nla_for_each_nested(nla, grp_attr, remaining) {
struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1];
int id = 0;
char *name = NULL;
err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla,
family_grp_policy);
if (err < 0) {
fprintf(stderr, "nla_parse_nested error! err = %d\n", err);
return -1;
}
if (tb[CTRL_ATTR_MCAST_GRP_ID])
id = *(const uint32_t *) nla_data(tb[CTRL_ATTR_MCAST_GRP_ID]);
if (tb[CTRL_ATTR_MCAST_GRP_NAME])
name = (char *) nla_data(tb[CTRL_ATTR_MCAST_GRP_NAME]);
if (id || name) {
*mcast_id = id;
*mcast_name = name;
}
}
}
return 0;
}
void genlmsg_recv(void) {
struct nlmsghdr *nlh;
struct nlattr *tb[__ATTR_MAX];
struct nlattr *nla_hdr;
int nla_length;
int ret = 0;
while(1)
{
memset(&nl_response_msg, 0, sizeof(nl_response_msg));
nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0);
if (nl_rxtx_length < 0) {
perror("recv()");
goto out;
}
nlh = &nl_response_msg.n;
nla_hdr = (struct nlattr *)((unsigned char *) nlh + NLMSG_HDRLEN + GENL_HDRLEN);
nla_length = nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;
if(ret = nla_parse(tb, __ATTR_MAX-1, nla_hdr, nla_length, NULL)) {
fprintf(stderr, "nla_parse error! ret = %d\n", ret);
goto out;
}
if (tb[1])
printf("ATTR_HELLO: len:%u type:%u data:%s\n", tb[1]->nla_len,
tb[1]->nla_type, (char *)nla_data(tb[1]));
else
printf("ATTR_HELLO: null\n");
if (tb[2])
printf("ATTR_FOO: len:%u type:%u data:%u\n", tb[2]->nla_len,
tb[2]->nla_type, *((__u32 *)nla_data(tb[2])));
else
printf("ATTR_FOO: null\n");
}
out:
return;
}
int main(void) {
struct nlattr *nla1, *nla2;
int len, rem, remaining;
struct nlmsghdr *nlh;
int family_id;
char *family_name;
int mcast_id;
char *mcast_name;
int err;
/* Step 1: Open the socket. Note that protocol = NETLINK_GENERIC */
nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (nl_fd < 0) {
perror("socket()");
return -1;
}
/* Step 2: Bind the socket. */
memset(&nl_address, 0, sizeof(nl_address));
nl_address.nl_family = AF_NETLINK;
nl_address.nl_groups = 0;
if (bind(nl_fd, (struct sockaddr *) &nl_address, sizeof(nl_address)) < 0) {
perror("bind()");
goto out;
}
/* Step 3: Resolve the family ID corresponding to the string GEN_FAMILY_STR */
/* Populate the netlink header */
nl_request_msg.n.nlmsg_type = GENL_ID_CTRL;
nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST;
nl_request_msg.n.nlmsg_seq = 0;
nl_request_msg.n.nlmsg_pid = getpid();
nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
/* Populate the payload's "family header" : which in our case is genlmsghdr */
nl_request_msg.g.cmd = CTRL_CMD_GETFAMILY;
nl_request_msg.g.version = 0x1;
/* Populate the payload's "netlink attributes" */
nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg); /* get location of genl data where to put */
nl_na->nla_type = CTRL_ATTR_FAMILY_NAME;
nl_na->nla_len = strlen(GEN_FAMILY_STR) + 1 + NLA_HDRLEN;
strcpy(NLA_DATA(nl_na), GEN_FAMILY_STR); /* Family name length can be upto 16 chars including \0 */
nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len);
memset(&nl_address, 0, sizeof(nl_address));
nl_address.nl_family = AF_NETLINK;
/* Send the family ID request message to the netlink controller */
nl_rxtx_length = sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len,
0, (struct sockaddr *) &nl_address, sizeof(nl_address));
if (nl_rxtx_length != nl_request_msg.n.nlmsg_len) {
perror("sendto()");
goto out;
}
/* Wait for the response message */
nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0);
if (nl_rxtx_length < 0) {
perror("recv()");
goto out;
}
/* Validate response message */
if (!NLMSG_OK((&nl_response_msg.n), nl_rxtx_length)) {
fprintf(stderr, "family ID request : invalid message\n");
goto out;
}
if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { /* error */
fprintf(stderr, "family ID request : receive error\n");
goto out;
}
/* Step 4: Extract family ID and mcast group ID*/
nlh = &nl_response_msg.n;
genlctrl_msg_parse(nlh, &family_id, &family_name, &mcast_id, &mcast_name);
printf("[INFO] family_id = %d, family_name = %s\n", family_id, family_name);
printf("[INFO] mcast_id = %d, mcast_name = %s\n", mcast_id, mcast_name);
/* Step 5: Add to mulitcast group */
err = setsockopt(nl_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &mcast_id, sizeof(mcast_id));
if (err < 0) {
perror ("setsockopt()");
goto out;
}
/* Step 6: Receive multicast data */
genlmsg_recv();
/* Step 7: Close the socket and quit */
close(nl_fd);
return 0;
out:
close(nl_fd);
return -1;
}
User Code Makefile
PWD := $(shell pwd)
TARGET := genl_ml
SRC := main.c
HDR_DIR = /usr/include/
LDFLAGS =
all:
gcc $(SRC) $(LDFLAGS) -o $(TARGET)
clean:
rm -fr $(TARGET)
I have 3 devices which are working in the similar way. I have a driver designed for one of the devices. I have added compatibility with
.compatible = "xyz,hmcSPI-0.00.a"
.compatible = "xyz,hmcSPI-1.00.a" and
.compatible = "xyz,hmcSPI-2.00.a"
It probes only the last device "xyz,hmcSPI-2.00.a" but first and second are seems to be disconnected. Meaning when I send data to the driver in /proc/hmcSPI0 then it reflects on only last device but not on first and second. When I remove third device "xyz,hmcSPI-2.00.a" from the device tree and driver then it detect "xyz,hmcSPI-1.00.a" but not "xyz,hmcSPI-0.00.a". Also, it shows probing physical address of the last device. I want to use this single driver for all three devices which are having different addresses.
Following is my platform driver.
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h> /* Needed for copy_from_user */
#include <asm/io.h> /* Needed for IO Read/Write Functions */
#include <linux/proc_fs.h> /* Needed for Proc File System Functions */
#include <linux/seq_file.h> /* Needed for Sequence File Operations */
#include <linux/platform_device.h> /* Needed for Platform Driver Functions */
#include<linux/slab.h>
/* Define Driver Name */
#define DRIVER_NAME "hmcSPI0"
unsigned long *base_addr; /* Vitual Base Address */
struct resource *res; /* Device Resource Structure */
unsigned long remap_size; /* Device Memory Size */
u16 slave_reg_add=0;
char hmcSPI0_phrase[16];
/* Write operation for /proc/hmcSPI0
* -----------------------------------
* When user cat a string to /proc/hmcSPI0 file, the string will be stored in
* const char __user *buf. This function will copy the string from user
* space into kernel space, and change it to an unsigned long value.
* It will then write the value to the register of hmcSPI0 controller.
*/
static ssize_t proc_hmcSPI0_write(struct file *file, const char __user * buf,
size_t count, loff_t * ppos)
{
u32 hmcSPI0_value;
if (count < 14) {
if (copy_from_user(hmcSPI0_phrase, buf, count))
return -EFAULT;
hmcSPI0_phrase[count] = '\0';
slave_reg_add=hmcSPI0_phrase[2]; //Copy first hex number which is offset to variable
hmcSPI0_phrase[2]=48; //Replace it by zero
if (slave_reg_add>=48){ //48='0' ASCII
slave_reg_add=(slave_reg_add-48); // //Convert the ACSII chr to Decimal Number
if(slave_reg_add<15){
hmcSPI0_value = simple_strtoul(hmcSPI0_phrase, NULL, 0);
wmb();
iowrite32(0x0, (base_addr+2)); //Clear Done slave reg2
iowrite32(hmcSPI0_value, (base_addr+slave_reg_add));
if(slave_reg_add == 0){
if((hmcSPI0_phrase[3]-48)==4)
iowrite32(0x4, (base_addr));
else if ((hmcSPI0_phrase[3]-48)==1 || (hmcSPI0_phrase[3]-48)==3){
while (ioread32((base_addr+2)) == 0);//wait untill transfer of all data
iowrite32(0x0, (base_addr+2));//Clear Done slave reg2
iowrite32(0x0, (base_addr));
}
else{}
}
}else {
slave_reg_add=hmcSPI0_phrase[3];
slave_reg_add=(slave_reg_add-48);
}
}
}
return count;
}
/* Callback function when opening file /proc/hmcSPI0
* ------------------------------------------------------
* Read the register value of hmcSPI0 controller, print the value to
* the sequence file struct seq_file *p. In file open operation for /proc/hmcSPI0
* this callback function will be called first to fill up the seq_file,
* and seq_read function will print whatever in seq_file to the terminal.
*/
static int proc_hmcSPI0_show(struct seq_file *p, void *v)
{
u32 hmcSPI0_value;
slave_reg_add=3;
hmcSPI0_value = ioread32((base_addr+slave_reg_add));
seq_printf(p, "Slave Reg=0x%x , Data=0x%x \n", slave_reg_add,hmcSPI0_value);
return 0;
}
/* Open function for /proc/hmcSPI0
* ------------------------------------
* When user want to read /proc/hmcSPI0 (i.e. cat /proc/hmcSPI0), the open function
* will be called first. In the open function, a seq_file will be prepared and the modprobe chdir no such file or directory zynq
* status of hmcSPI0 will be filled into the seq_file by proc_hmcSPI0_show function.
*/
static int proc_hmcSPI0_open(struct inode *inode, struct file *file)
{
unsigned int size = 16;
char *buf;
struct seq_file *m;
int res;
buf = (char *)kmalloc(size * sizeof(char), GFP_KERNEL);
if (!buf)
return -ENOMEM;
res = single_open(file, proc_hmcSPI0_show, NULL);
if (!res) {
m = file->private_data;
m->buf = buf;
m->size = size;
} else {
kfree(buf);
}
return res;
}
/* File Operations for /proc/hmcSPI0 */
static const struct file_operations proc_hmcSPI0_operations = {
.open = proc_hmcSPI0_open,
.read = seq_read,
.write = proc_hmcSPI0_write,
.llseek = seq_lseek,
.release = single_release
};
/* Shutdown function for hmcSPI0
* -----------------------------------
* Before hmcSPI0 shutdown, clear the data
*/
static void hmcSPI0_shutdown(struct platform_device *pdev)
{
iowrite32(0, base_addr);
}
/* Remove function for hmcSPI0
* ----------------------------------
* When hmcSPI0 module is removed, clear all data first,
* release virtual address and the memory region requested.
*/
static int hmcSPI0_remove(struct platform_device *pdev)
{
hmcSPI0_shutdown(pdev);
/* Remove /proc/hmcSPI0 entry */
remove_proc_entry(DRIVER_NAME, NULL);
/* Release mapped virtual address */
iounmap(base_addr);
/* Release the region */
release_mem_region(res->start, remap_size);
return 0;
}
/* Device Probe function for hmcSPI0
* ------------------------------------
* Get the resource structure from the information in device tree.
* request the memory regioon needed for the controller, and map it into
* kernel virtual memory space. Create an entry under /proc file system
* and register file operations for that entry.
*/
static int hmcSPI0_probe(struct platform_device *pdev)
{
struct proc_dir_entry *hmcSPI0_proc_entry;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "No memory resource\n");
return -ENODEV;
}
remap_size = res->end - res->start + 1;
if (!request_mem_region(res->start, remap_size, pdev->name)) {
dev_err(&pdev->dev, "Cannot request IO\n");
return -ENXIO;
}
base_addr = ioremap(res->start, remap_size);
if (base_addr == NULL) {
dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n",
(unsigned long)res->start);
ret = -ENOMEM;
goto err_release_region;
}
hmcSPI0_proc_entry = proc_create(DRIVER_NAME, 0, NULL,
&proc_hmcSPI0_operations);
if (hmcSPI0_proc_entry == NULL) {
dev_err(&pdev->dev, "Couldn't create proc entry\n");
ret = -ENOMEM;
goto err_create_proc_entry;
}
printk(KERN_INFO DRIVER_NAME " probed at VA 0x%08lx\n",
(unsigned long) base_addr);
printk(KERN_INFO DRIVER_NAME " probed at PA 0x%08lx\n",
(unsigned long)res->start);
return 0;
err_create_proc_entry:
iounmap(base_addr);
err_release_region:
release_mem_region(res->start, remap_size);
return ret;
}
/* device match table to match with device node in device tree */
static const struct of_device_id hmcSPI0_of_match[] = {
{.compatible = "xyz,hmcSPI-0.00.a"},
{.compatible = "xyz,hmcSPI-1.00.a"},
{.compatible = "xyz,hmcSPI-2.00.a"},
{},
};
MODULE_DEVICE_TABLE(of, hmcSPI0_of_match);
/* platform driver structure for hmcSPI0 driver */
static struct platform_driver hmcSPI0_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = hmcSPI0_of_match},
.probe = hmcSPI0_probe,
.remove = hmcSPI0_remove,
.shutdown = hmcSPI0_shutdown
};
/* Register hmcSPI0 platform driver */
module_platform_driver(hmcSPI0_driver);
/* Module Informations */
MODULE_AUTHOR("Ganesh Kalbhor, xyz.");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRIVER_NAME ": hmcSPI0 driver (Simple Version)");
MODULE_ALIAS(DRIVER_NAME);
device tree entry is as follows
/ {
model = "Zynq Zed Development Board";
compatible = "xlnx,zynq-zed", "xlnx,zynq-7000";
aliases {
ethernet0 = &gem0;
serial0 = &uart1;
spi0 = &qspi;
};
memory {
device_type = "memory";
reg = <0x0 0x20000000>;
};
chosen {
bootargs = "console=ttyPS0,115200 root=/dev/ram rw earlyprintk";
linux,stdout-path = &uart1;
stdout-path = &uart1;
};
usb_phy0: phy0 {
compatible = "usb-nop-xceiv";
#phy-cells = <0>;
};
hmcSPI0 {
compatible = "xyz,hmcSPI-0.00.a";
reg = <0x43C00000 0x1000>;
};
hmcSPI0 {
compatible = "xyz,hmcSPI-1.00.a";
reg = <0x43C10000 0x1000>;
};
hmcSPI0 {
compatible = "xyz,hmcSPI-2.00.a";
reg = <0x43C20000 0x1000>;
};
};
Please help me to resolve this problem.
I want to stream JPEG images or motion-JPEG file through live 555. But the problem is that in live 555 implementation for Jpegs is not available. Anyone can help ??
You can find a implementation that was posted to the devel mailing list http://lists.live555.com/pipermail/live-devel/2012-February/014672.html.
The code and a sample is available but this modification was rejected by live555 maintainer.
First we need to implement an MJPEGVideoSource than can feed a JPEGVideoRTPSink.
#include "JPEGVideoSource.hh"
class MJPEGVideoSource : public JPEGVideoSource
{
public:
static MJPEGVideoSource* createNew (UsageEnvironment& env, FramedSource* source)
{
return new MJPEGVideoSource(env,source);
}
virtual void doGetNextFrame()
{
if (m_inputSource)
m_inputSource->getNextFrame(fTo, fMaxSize, afterGettingFrameSub, this, FramedSource::handleClosure, this);
}
virtual void doStopGettingFrames()
{
FramedSource::doStopGettingFrames();
if (m_inputSource)
m_inputSource->stopGettingFrames();
}
static void afterGettingFrameSub(void* clientData, unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds)
{
MJPEGVideoSource* source = (MJPEGVideoSource*)clientData;
source->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
}
void afterGettingFrame(unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds)
{
int headerSize = 0;
bool headerOk = false;
fFrameSize = 0;
for (unsigned int i = 0; i < frameSize ; ++i)
{
// SOF
if ( (i+8) < frameSize && fTo[i] == 0xFF && fTo[i+1] == 0xC0 )
{
m_height = (fTo[i+5]<<5)|(fTo[i+6]>>3);
m_width = (fTo[i+7]<<5)|(fTo[i+8]>>3);
}
// DQT
if ( (i+5+64) < frameSize && fTo[i] == 0xFF && fTo[i+1] == 0xDB)
{
if (fTo[i+4] ==0)
{
memcpy(m_qTable, fTo + i + 5, 64);
m_qTable0Init = true;
}
else if (fTo[i+4] ==1)
{
memcpy(m_qTable + 64, fTo + i + 5, 64);
m_qTable1Init = true;
}
}
// End of header
if ( (i+1) < frameSize && fTo[i] == 0x3F && fTo[i+1] == 0x00 )
{
headerOk = true;
headerSize = i+2;
break;
}
}
if (headerOk)
{
fFrameSize = frameSize - headerSize;
memmove( fTo, fTo + headerSize, fFrameSize );
}
fNumTruncatedBytes = numTruncatedBytes;
fPresentationTime = presentationTime;
fDurationInMicroseconds = durationInMicroseconds;
afterGetting(this);
}
virtual u_int8_t type() { return 1; };
virtual u_int8_t qFactor() { return 128; };
virtual u_int8_t width() { return m_width; };
virtual u_int8_t height() { return m_height; };
u_int8_t const* quantizationTables( u_int8_t& precision, u_int16_t& length )
{
length = 0;
precision = 0;
if ( m_qTable0Init && m_qTable1Init )
{
precision = 8;
length = sizeof(m_qTable);
}
return m_qTable;
}
protected:
MJPEGVideoSource(UsageEnvironment& env, FramedSource* source) : JPEGVideoSource(env),
m_inputSource(source),
m_width(0),
m_height(0),
m_qTable0Init(false),
m_qTable1Init(false)
{
memset(&m_qTable,0,sizeof(m_qTable));
}
virtual ~MJPEGVideoSource()
{
Medium::close(m_inputSource);
}
protected:
FramedSource* m_inputSource;
u_int8_t m_width;
u_int8_t m_height;
u_int8_t m_qTable[128];
bool m_qTable0Init;
bool m_qTable1Init;
};
Next we can use it as a video source in order to build a simple RTSP server:
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
#include "MJPEGVideoSource.hh"
char const* inputFileName = "test.mjpeg";
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
const unsigned short rtpPortNum = 18888;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 255;
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
rtpGroupsock.multicastSendOnly(); // we're a SSM source
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
rtcpGroupsock.multicastSendOnly(); // we're a SSM source
// Create a 'JPEG Video RTP' sink from the RTP 'groupsock':
RTPSink* videoSink = JPEGVideoRTPSink::createNew(*env, &rtpGroupsock);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 5000; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
RTCPInstance* rtcp = RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
videoSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms = ServerMediaSession::createNew(*env, "testStream", inputFileName,"Session streamed by \"testMJPEGVideoStreamer\"",
True /*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
*env << "Beginning streaming...\n";
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(*env, inputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
// Create the MJPEG video source:
MJPEGVideoSource* videoSource = MJPEGVideoSource::createNew(*env, fileSource);
// Finally, start playing:
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, NULL, NULL);
env->taskScheduler().doEventLoop();
return 0;
}
Hope you have done it but if not-
see this Jpeg Streaming using live555
This is doing the same thing as you have asked to stream the images/Jpegs.
For MJpegs you'll have to do the same process.