I'm using Leadtools 20 API to control the scanner and scan some documents. I have 2 questions.1 - When using L_TwainAcquire with a callback function that receives the images from the scanner and keeps returning SUCCESS to get the next image. Is there anyway inside the callback function to determine if the image is from the front camera or the back camera?2 - Is there a way to force the scanner to use only the back camera for scanning?
Thank youSam
If you mean the 2 sides of a scanned document when using a duplex scanner, the answer is as follows:
To know which side of the paper is currently being processed in the bitmap callback, the following code can be used inside the callback function:
L_INT EXT_CALLBACK LTwainBitmapCallback(HTWAINSESSION hSession, pBITMAPHANDLE pBitmap, L_VOID * pUserData)
{
L_INT nRet = SUCCESS;
TW_EXTIMAGEINFO* pExtImg = NULL;
int nExtraInfoAftreZero = 0; // we have only one info, so no extra needed
pExtImg = (TW_EXTIMAGEINFO *)malloc(sizeof TW_EXTIMAGEINFO + nExtraInfoAftreZero * sizeof TW_INFO);
if (pExtImg)
{
pExtImg->NumInfos = 1;
pExtImg->Info[0].InfoID = TWEI_PAGESIDE;
pExtImg->Info[0].ItemType = TWTY_UINT16;
nRet = L_TwainGetExtendedImageInfo (hSession, pExtImg);
if (nRet == SUCCESS)
{
// Do processing to returned values
if(pExtImg->Info[0].Item == TWCS_TOP)
OutputDebugString("Front of sheet.\n");
else if (pExtImg->Info[0].Item == TWCS_BOTTOM)
OutputDebugString("Rear of sheet.\n");
else
OutputDebugString("Unexpected!\n");
nRet = L_TwainFreeExtendedImageInfoStructure (&pExtImg);
if(nRet != SUCCESS)
return nRet;
}
free(pExtImg);
}
return SUCCESS;
}
Forcing a specific side to scan can be done using a combination of CAP_CAMERASIDE and CAP_CAMERAENABLED capabilities, as explained in the Twain Spec.
Quoting from that document:
To enable bottom only scanning, set CAP_CAMERASIDE to TWCS_BOTTOM and
set CAP_CAMERAENABLED to TRUE, then set CAP_CAMERASIDE to TWCS_TOP and
set CAP_CAMERAENABLED to FALSE.
If you mean you have a computer with 2 Twain camera sources (back and front), the answer becomes as follows:
To know which Twain source (e.g. camera) triggered the Twain Bitmap Callback when L_TwainAcquire() was called, you can use the
L_TwainGetSources() function with the LTWAIN_SOURCE_ENUMERATE_DEFAULT flag to get the currently-selected Twain device. This can be done inside the Bitmap Callback as shown in the following code:
// Source Info Callback
L_INT EXT_CALLBACK TwainSourceInfoCallbackCurrent(HTWAINSESSION hSession, pLTWAINSOURCEINFO pSourceInfo, L_VOID * pUserData)
{
strcpy((L_CHAR *)pUserData, pSourceInfo->pszTwnSourceName);
return SUCCESS;
}
// Twain Bitmap Callback
L_INT EXT_CALLBACK LTwainBitmapCallback(HTWAINSESSION hSession, pBITMAPHANDLE pBitmap, L_VOID * pUserData)
{
char szSourceName[1024];
L_TwainGetSources(hSession, TwainSourceInfoCallbackCurrent, sizeof LTWAINSOURCEINFO, LTWAIN_SOURCE_ENUMERATE_DEFAULT, szSourceName);
// Now szSourceName contains name of Twain Source.
return SUCCESS;
}
Yes, you can force scanning from a specific device (scanner, camera, etc.) by sending its name to the L_TwainSelectSource() function before scanning, as follows:
LTWAINSOURCE TwainSource;
TwainSource.uStructSize = sizeof LTWAINSOURCE;
TwainSource.pszTwainSourceName = pszRearCameraTwainSourceName;
L_TwainSelectSource(twainSession, &TwainSource);
// Now L_TwainAcquire() will capture from Rear Camera
To find the name of all devices, you can use this code:
L_TwainGetSources(twainSession, TwainSourceInfoCallback, sizeof LTWAINSOURCEINFO, LTWAIN_SOURCE_ENUMERATE_ALL, NULL);
The Source Info Callback will be triggered once for each Twain source. It can be implemented like this:
L_INT EXT_CALLBACK TwainSourceInfoCallback(HTWAINSESSION hSession, pLTWAINSOURCEINFO pSourceInfo, L_VOID * pUserData)
{
OutputDebugString(pSourceInfo->pszTwnSourceName); // You can save the names of Twain Sources into global variables if you like
OutputDebugString("\n");
return SUCCESS;
}
Related
I'm trying to use the Leadtools API version 21 for automatically scanning some documents while setting some properties from code (do not want to show the TWAIN dialog). for example I set the scan DPI to 300 using L_TwainSetResolution(), but the image I get inside the bitmap callback always has resolution of 96x96.Here is a sudo code of what I have done (it runs in a secondary thread and the unlock has been done in the main thread):
void CheckRetCode(int rc)
{
if (SUCCESS != rc)
{
L_TCHAR errMsg[1024];
memset(errMsg, 0, sizeof(errMsg));
L_GetFriendlyErrorMessage(rc, errMsg, 1024, L_FALSE);
throw TLeadException(errMsg, rc);
}
}
L_INT EXT_CALLBACK GetBmpCB(HTWAINSESSION hS, pBITMAPHANDLE pBitmap, L_VOID* pUserData)
{
// in here pBitmap->XResolution and pBitmap->YResolution are always 96
// but I have clearly set them to 300
// process image here
L_FreeBitmap(pBitmap); // free the image
return SUCCESS;
}
void OnThreadExecute(void)
{
HTWAINSESSION hSession = nullptr;
APPLICATIONDATA appData;
L_INT nRet;
L_TCHAR pszTwnSourceName[1024];
LTWAINSOURCE sInfo;
TW_FIX32 XRes = L_TwainFloatToFix32(300.0);
TW_FIX32 YRes = L_TwainFloatToFix32(300.0);
BITMAPHANDLE tBmp;
memset(&tBmp, 0, sizeof(BITMAPHANDLE));
tBmp.uStructSize = sizeof(BITMAPHANDLE);
memset(&appData, 0, sizeof(APPLICATIONDATA));
appData.uStructSize = sizeof(APPLICATIONDATA);
appData.hWnd = hWnd;// hWnd is valid handle of my main window
appData.uLanguage = TWLG_ENGLISH_USA;
appData.uCountry = TWCY_USA;
wcscpy(appData.szManufacturerName, L"MyCompanyName");
wcscpy(appData.szAppProductFamily, L"MyProductName");
wcscpy(appData.szAppName, appData.szAppProductFamily);
wcscpy(appData.szVersionInfo, L"Version 0.1.0.1");
nRet = L_TwainInitSession2(&hSession, &appData, LTWAIN_INIT_MULTI_THREADED);
CheckRetCode(nRet);here
memset(pszTwnSourceName, 0, sizeof(pszTwnSourceName));
wcscpy(pszTwnSourceName, L"EPSON Artisan837/PX830");
sInfo.uStructSize = sizeof(LTWAINSOURCE);
sInfo.pszTwainSourceName = pszTwnSourceName;
CheckRetCode(L_TwainSelectSource(hSession, &sInfo));
CheckRetCode(L_TwainStartCapsNeg(hSession));
CheckRetCode(L_TwainSetImageUnit(hSession, TWUN_INCHES));
CheckRetCode(L_TwainEnableDuplex(hSession, FALSE));
CheckRetCode(L_TwainSetResolution(hSession, &XRes, &YRes)); // setting the res to 300 x 300
CheckRetCode(L_TwainEndCapsNeg(hSession));
L_TwainAcquire(hSession, &tBmp, sizeof(BITMAPHANDLE), GetBmpCB, 0, NULL, NULL);
if(tBmp.Flags.Allocated)
L_FreeBitmap(&tBmp);
}
By the way the scanned image has the correct number of pixels. If I scan a 8.5x11 page, I get an image that is 2550x3300 pixels, but XResolution and YResolution are set to 96 which causes the saved image to be 26.5"x34.375".
Thank you
Sam
I tested with 4 different Twain drivers and got the following results:
One of them did not support 300 DPI, so it returned error “Bad value” when calling L_TwainSetResolution(). However, it returned correct image size for the actual DPI it supported, which is 100.
The other 3 supported different DPI values, and returned correct image size and DPI values in the callback’s pBitmap.
The only major difference between my code and yours is that I called L_TwainEndSession(). If your code doesn’t include it, make sure to call it once for each call of L_TwainInitSession/L_TwainInitSession2.
If that’s not the cause of your problem, try to test with more than one Twain driver and see if the problem is specific to one driver. If it’s not, put your code in a small test program and email it to support#leadtools.com and we will check it for you.
I'm trying to visualize a soundwave captured by WASAPI loopback but find that the packets I record do not form a smooth wave when put together.
My understanding of how the WASAPI capture client works is that when I call pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL) the buffer pData is filled from the front with numFramesAvailable datapoints. Each datapoint is a float and they alternate by channel. Thus to get all available datapoints I should cast pData to a float pointer, and take the first channels * numFramesAvailable values. Once I release the buffer and call GetBuffer again it provides the next packet. I would assume that these packets would follow on from each other but it doesn't seem to be the case.
My guess is that either I'm making an incorrect assumption about the format of the audio data in pData or the capture client is either missing or overlapping frames. But have no idea how to check these.
To make the code below as brief as possible I've removed things like error status checking and cleanup.
Initialization of capture client:
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
pAudioClient = NULL;
IMMDeviceEnumerator * pDeviceEnumerator = NULL;
IMMDevice * pDeviceEndpoint = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
int channels;
// Initialize audio device endpoint
CoInitialize(nullptr);
CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator );
pDeviceEnumerator ->GetDefaultAudioEndpoint(eRender, eConsole, &pDeviceEndpoint );
// init audio client
WAVEFORMATEX *pwfx = NULL;
REFERENCE_TIME hnsRequestedDuration = 10000000;
REFERENCE_TIME hnsActualDuration;
audio_device_endpoint->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
pAudioClient->GetMixFormat(&pwfx);
pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, hnsRequestedDuration, 0, pwfx, NULL);
channels = pwfx->nChannels;
pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
pAudioClient->Start(); // Start recording.
Capture of packets (note that std::mutex packet_buffer_mutex and vector<vector<float>> packet_bufferare already be defined and used by another thread to safely display the data):
UINT32 packetLength = 0;
BYTE *pData = NULL;
UINT32 numFramesAvailable;
DWORD flags;
int max_packets = 8;
std::unique_lock<std::mutex>write_guard(packet_buffer_mutex, std::defer_lock);
while (true) {
pCaptureClient->GetNextPacketSize(&packetLength);
while (packetLength != 0)
{
// Get the available data in the shared buffer.
pData = NULL;
pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL; // Tell CopyData to write silence.
}
write_guard.lock();
if (packet_buffer.size() == max_packets) {
packet_buffer.pop_back();
}
if (pData) {
float * pfData = (float*)pData;
packet_buffer.emplace(packet_buffer.begin(), pfData, pfData + channels * numFramesAvailable);
} else {
packet_buffer.emplace(packet_buffer.begin());
}
write_guard.unlock();
hpCaptureClient->ReleaseBuffer(numFramesAvailable);
pCaptureClient->GetNextPacketSize(&packetLength);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
I store the packets in a vector<vector<float>> (where each vector<float> is a packet) removing the last one and inserting the newest at the start so I can iterate over them in order.
Below is the result of a captured sinewave, plotting alternating values so it only represents a single channel. It is clear where the packets are being stitched together.
Something is playing a sine wave to Windows; you're recording the sine wave back in the audio loopback; and the sine wave you're getting back isn't really a sine wave.
You're almost certainly running into glitches. The most likely causes of glitching are:
Whatever is playing the sine wave to Windows isn't getting data to Windows in time, so the buffer is running dry.
Whatever is reading the loopback data out of Windows isn't reading the data in time, so the buffer is filling up.
Something is going wrong in between playing the sine wave to Windows and reading it back.
It is possible that more than one of these are happening.
The IAudioCaptureClient::GetBuffer call will tell you if you read the data too late. In particular it will set *pdwFlags so that the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY bit is set.
Looking at your code, I see you're doing the following things between the GetBuffer and the WriteBuffer:
Waiting on a lock
Sometimes doing something called "pop_back"
Doing something called "emplace"
I quote from the above-linked documentation:
Clients should avoid excessive delays between the GetBuffer call that acquires a packet and the ReleaseBuffer call that releases the packet. The implementation of the audio engine assumes that the GetBuffer call and the corresponding ReleaseBuffer call occur within the same buffer-processing period. Clients that delay releasing a packet for more than one period risk losing sample data.
In particular you should NEVER DO ANY OF THE FOLLOWING between GetBuffer and ReleaseBuffer because eventually they will cause a glitch:
Wait on a lock
Wait on any other operation
Read from or write to a file
Allocate memory
Instead, pre-allocate a bunch of memory before calling IAudioClient::Start. As each buffer arrives, write to this memory. On the side, have a regularly scheduled work item that takes written memory and writes it to disk or whatever you're doing with it.
I have a Directshow File Source Filter which has audio and frame output pins. It is written in C++ based on this tutorial on MSDN. My filter opens the video by using Medialooks MFormats SDK and provides raw data to output pins. Two pins are directly connecting to renderer filters when they are rendered.
The problem occurs when I run the graph, pause the video and seek to the frame number 0. After a call to ChangeStart method in output frame pin, sometimes FillBuffer is called three times and frame 1 is shown on the screen instead of 0. When it is called two times, it shows the correct frame which is the frame 0.
Output pins are inherited from CSourceStream and CSourceSeeking classes. Here is my FillBuffer and ChangeStart methods of the output frame pin;
FillBuffer Method
HRESULT frame_pin::FillBuffer(IMediaSample *sample)
{
CheckPointer(sample, E_POINTER);
BYTE *frame_buffer;
sample->GetPointer(&frame_buffer);
// Check if the downstream filter is changing the format.
CMediaType *mt;
HRESULT hr = sample->GetMediaType(reinterpret_cast<AM_MEDIA_TYPE**>(&mt));
if (hr == S_OK)
{
auto new_width = reinterpret_cast<VIDEOINFOHEADER2*>(mt->pbFormat)->bmiHeader.biWidth;
auto old_witdh = reinterpret_cast<VIDEOINFOHEADER2*>(m_mt.pbFormat)->bmiHeader.biWidth;
if(new_width != old_witdh)
format_changed_ = true;
SetMediaType(mt);
DeleteMediaType(mt);
}
ASSERT(m_mt.formattype == FORMAT_VideoInfo2);
VIDEOINFOHEADER2 *vih = reinterpret_cast<VIDEOINFOHEADER2*>(m_mt.pbFormat);
CComPtr<IMFFrame> mf_frame;
{
CAutoLock lock(&shared_state_);
if (source_time_ >= m_rtStop)
return S_FALSE;
// mf_reader_ is a member external SDK instance which gets the frame data with this function call
hr = mf_reader_->SourceFrameConvertedGetByNumber(&av_props_, frame_number_, -1, &mf_frame, CComBSTR(L""));
if (FAILED(hr))
return hr;
REFERENCE_TIME start, stop = 0;
start = stream_time_;
stop = static_cast<REFERENCE_TIME>(tc_.get_stop_time() / m_dRateSeeking);
sample->SetTime(&start, &stop);
stream_time_ = stop;
source_time_ += (stop - start);
frame_number_++;
}
if (format_changed_)
{
CComPtr<IMFFrame> mf_frame_resized;
mf_frame->MFResize(eMFCC_YUY2, std::abs(vih->bmiHeader.biWidth), std::abs(vih->bmiHeader.biHeight), 0, &mf_frame_resized, CComBSTR(L""), CComBSTR(L""));
mf_frame = mf_frame_resized;
}
MF_FRAME_INFO mf_frame_info;
mf_frame->MFAllGet(&mf_frame_info);
memcpy(frame_buffer, reinterpret_cast<BYTE*>(mf_frame_info.lpVideo), mf_frame_info.cbVideo);
sample->SetActualDataLength(static_cast<long>(mf_frame_info.cbVideo));
sample->SetSyncPoint(TRUE);
sample->SetPreroll(FALSE);
if (discontinuity_)
{
sample->SetDiscontinuity(TRUE);
discontinuity_ = FALSE;
}
return S_OK;
}
ChangeStart Method
HRESULT frame_pin::ChangeStart()
{
{
CAutoLock lock(CSourceSeeking::m_pLock);
tc_.reset();
stream_time_ = 0;
source_time_ = m_rtStart;
frame_number_ = static_cast<int>(m_rtStart / frame_lenght_);
}
update_from_seek();
return S_OK;
}
From the Microsoft DirectShow documentation:
The CSourceSeeking class is an abstract class for implementing
seeking in source filters with one output pin.
CSourceSeeking is not recommended for a filter with more than one
output pin. The main issue is that only one pin should respond to
seeking requests. Typically this requires communication among the pins
and the filter.
And you have two output pins in your source filter.
The CSourceSeeking class can be extended to manage more than one output pin with custom coding. When seek commands come in they'll come through both input pins so you'll need to decide which pin is controlling seeking and ignore seek commands arriving at the other input pin.
I am converting a project to a UWP App, and thus have been following guidelines outlined in the MSDN post here. The existing project heavily relies on CreateFile() to communicate with connected devices.
There are many posts in SO that show us how to get a CreateFile()-accepted device path using SetupAPI's SetupDiGetDeviceInterfaceDetail() Is there an alternative way to do this using the PnP Configuration Manager API? Or an alternative, user-mode way at all?
I had some hope when I saw this example in Windows Driver Samples github, but quickly became dismayed when I saw that the function they used in the sample is ironically not intended for developer use, as noted in this MSDN page.
function GetDevicePath in general correct and can be used as is. about difference between CM_*(..) and CM_*_Ex(.., HMACHINE hMachine) - the CM_*(..) simply call CM_*_Ex(.., NULL) - so for local computer versions with and without _Ex suffix the same.
about concrete GetDevicePath code - call CM_Get_Device_Interface_List_Size and than CM_Get_Device_Interface_List only once not 100% correct - because between this two calls new device with this interface can be arrived to system and buffer size returned by CM_Get_Device_Interface_List_Size can be already not enough for CM_Get_Device_Interface_List. of course possibility of this very low, and you can ignore this. but i prefer make code maximum theoretical correct and call this in loop, until we not receive error other than CR_BUFFER_SMALL. also need understand that CM_Get_Device_Interface_List return multiple, NULL-terminated Unicode strings - so we need iterate here. in [example] always used only first returned symbolic link name of an interface instance. but it can be more than 1 or at all - 0 (empty). so better name function - GetDevicePaths - note s at the end. i be use code like this:
ULONG GetDevicePaths(LPGUID InterfaceClassGuid, PWSTR* pbuf)
{
CONFIGRET err;
ULONG len = 1024;//first try with some reasonable buffer size, without call *_List_SizeW
for(PWSTR buf;;)
{
if (!(buf = (PWSTR)LocalAlloc(0, len * sizeof(WCHAR))))
{
return ERROR_NO_SYSTEM_RESOURCES;
}
switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
case CR_BUFFER_SMALL:
err = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
default:
LocalFree(buf);
if (err)
{
return CM_MapCrToWin32Err(err, ERROR_UNIDENTIFIED_ERROR);
}
continue;
case CR_SUCCESS:
*pbuf = buf;
return NOERROR;
}
}
}
and usage example:
void example()
{
PWSTR buf, sz;
if (NOERROR == GetDevicePaths((GUID*)&GUID_DEVINTERFACE_VOLUME, &buf))
{
sz = buf;
while (*sz)
{
DbgPrint("%S\n", sz);
HANDLE hFile = CreateFile(sz, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
// do something
CloseHandle(hFile);
}
sz += 1 + wcslen(sz);
}
LocalFree(buf);
}
}
so we must not simply use in returned DevicePathS (sz) only first string, but iterate it
while (*sz)
{
// use sz
sz += 1 + wcslen(sz);
}
I got a valid Device Path to a USB Hub Device, and used it successfully to get various device descriptors by sending some IOCTLs, by using the function I posted in my own answer to another question
I'm reporting the same function below:
This function returns a list of NULL-terminated Device Paths (that's what we get from CM_Get_Device_Interface_List())
You need to pass it the DEVINST, and the wanted interface GUID.
Since both the DEVINST and interface GUID are specified, it is highly likely that CM_Get_Device_Interface_List() will return a single Device Path for that interface, but technically you should be prepared to get more than one result.
It is responsibility of the caller to delete[] the returned list if the function returns successfully (return code 0)
int GetDevInstInterfaces(DEVINST dev, LPGUID interfaceGUID, wchar_t**outIfaces, ULONG* outIfacesLen)
{
CONFIGRET cres;
if (!outIfaces)
return -1;
if (!outIfacesLen)
return -2;
// Get System Device ID
WCHAR sysDeviceID[256];
cres = CM_Get_Device_ID(dev, sysDeviceID, sizeof(sysDeviceID) / sizeof(sysDeviceID[0]), 0);
if (cres != CR_SUCCESS)
return -11;
// Get list size
ULONG ifaceListSize = 0;
cres = CM_Get_Device_Interface_List_Size(&ifaceListSize, interfaceGUID, sysDeviceID, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cres != CR_SUCCESS)
return -12;
// Allocate memory for the list
wchar_t* ifaceList = new wchar_t[ifaceListSize];
// Populate the list
cres = CM_Get_Device_Interface_List(interfaceGUID, sysDeviceID, ifaceList, ifaceListSize, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cres != CR_SUCCESS) {
delete[] ifaceList;
return -13;
}
// Return list
*outIfaces = ifaceList;
*outIfacesLen = ifaceListSize;
return 0;
}
Please note that, as RbMm already said in his answer, you may get a CR_BUFFER_SMALL error from the last CM_Get_Device_Interface_List() call, since the device list may have been changed in the time between the CM_Get_Device_Interface_List_Size() and CM_Get_Device_Interface_List() calls.
I started writing a code to handle wifi card using iwconfig/ioctl when I realised that it is depricated and most of applications uses nl80211.
I started reading its source code but there is no docs and code is a bit complicated.
How can I do simple things like scanning, turning off/on, setting card mode using nl80211 or libnl?
This is what I started with iw:
void set_card_mode(MODE mode, std::string ifname)
{
int skfd = iw_sockets_open();
struct iwreq wrq;
wrq.u.mode = static_cast<unsigned int>(mode);
power_interface(ifname, false);
if(iw_set_ext(skfd, ifname.c_str(), SIOCSIWMODE, &wrq) < 0)
throw std::runtime_error("Can set card mode");
}
MODE get_card_mode(std::string ifname)
{
int skfd = iw_sockets_open();
struct iwreq wrq;
if (iw_get_ext (skfd, ifname.c_str(), SIOCGIWMODE, &wrq) >= 0)
{
return static_cast<MODE>(wrq.u.mode);
}
}
Is there any equivalent of iw_get_ext to set/get wifi interface or any api with simple functions like "set_mode" or "power_off"?
I scan with netlink using the following steps
Prepare and execute NL80211_CMD_TRIGGER_SCAN
Prepare and execute NL80211_CMD_GET_SCAN
Together with NL80211_CMD_GET_SCAN a callback is registered.
In the callback the raw data are parsed as BSS. The IE's are parsed to. See enums with NL80211_BSS_MAX, NL80211_ATTR_MAX from nl80211.h.
Check the return values of every netlink call before process the next step.
Code snippets:
nl_sock* socket = nl_socket_alloc();
genl_connect(socket);
struct nl_msg* msg = nlmsg_alloc();
int driverId = genl_ctrl_resolve(socket, "nl80211");
genlmsg_put(msg, 0, 0, driverId, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0);
and fetch with:
genlmsg_put(msg, 0, 0, driverId, 0, NLM_F_DUMP, NL80211_CMD_GET_SCAN, 0);
nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, onScanResult, null);
My callback starts with:
struct genlmsghdr* msgHeader = (genlmsghdr*)nlmsg_data(nlmsg_hdr(msg));
struct nlattr* attributes[NL80211_ATTR_MAX + 1];
struct nlattr* bss[NL80211_BSS_MAX + 1];
if(nla_parse(attributes, NL80211_ATTR_MAX, genlmsg_attrdata(msgHeader, 0), genlmsg_attrlen(msgHeader, 0), NULL) == 0)
{
// Read the attributes
// and check for NL80211_ATTR_BSS != 0
}
Most of the scanning results did I found in NL80211_BSS_INFORMATION_ELEMENTS.
if (nla_parse_nested(bss, NL80211_BSS_MAX, attributes[NL80211_ATTR_BSS], bss_policy) == 0)
{ /* read the bss attributes */ }
See NL80211_BSS_INFORMATION_ELEMENTS from nl80211.h and
But I failed to check for WEP privacy.
To check for WPA or WPA2 is easy, because there is an extra IE element with ID 48 (Śee IEEE Std 802.11 2012, chapter 8.4.2, free download form ieee side)