├── LICENSE ├── Makefile ├── Makefile.winegcc ├── README.md ├── dab_install.sh ├── main.c ├── rtl283xaccess.def └── rtltcpaccess.tar.gz /LICENSE: -------------------------------------------------------------------------------- 1 | rtltcpaccess, a drop-in replacement for RTL283XACCESS.dll 2 | https://github.com/steve-m/rtltcpaccess 3 | 4 | Copyright (c) 2013 Steve Markgraf 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # MinGW Makefile for rtltcpaccess 2 | 3 | all : main 4 | 5 | main: 6 | gcc -O3 -Wall -Wno-unused-function -c main.c -o main.o 7 | # windres -i resource.rc -o resource.o 8 | # gcc -o RTL283XACCESS.dll main.o resource.o -shared -s -lws2_32 -Wl,--subsystem,windows,--out-implib,libaddlib.a 9 | gcc -o RTL283XACCESS.dll main.o -shared -s -lws2_32 -Wl,--subsystem,windows 10 | 11 | clean: 12 | rm *.o *.dll 13 | -------------------------------------------------------------------------------- /Makefile.winegcc: -------------------------------------------------------------------------------- 1 | # winegcc Makefile for rtltcpaccess 2 | 3 | all : main 4 | 5 | main: 6 | winegcc -m32 -mno-cygwin -O3 -Wall -Wno-unused-function -o main.o -c main.c 7 | winegcc -m32 -mno-cygwin -shared -s -o RTL283XACCESS.dll rtl283xaccess.def main.o -lws2_32 8 | 9 | clean: 10 | rm *.o *.dll.so 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rtltcpaccess 2 | ============ 3 | 4 | rtltcpaccess is a drop-in replacement for RTL283XACCESS.dll that connects 5 | to an rtl_tcp server and streams the samples from there. 6 | Its main purpose is to allow DAB reception on GNU/Linux and OS X through 7 | running the Windows software in Wine, but it works on Windows as well. 8 | 9 | 10 | Installation 11 | --- 12 | 13 | The installation script (dab_install.sh) fetches the DAB software as well as a 14 | pre-built version of the library which is also available from here: 15 | 16 | https://raw.github.com/steve-m/rtltcpaccess/master/rtltcpaccess.tar.gz 17 | 18 | To get the installation script, run: 19 | 20 | wget https://raw.github.com/steve-m/rtltcpaccess/master/dab_install.sh 21 | chmod +x dab_install.sh 22 | ./dab_install.sh 23 | 24 | If you want to build the library yourself, get MinGW and run 'make'. 25 | 26 | Usage 27 | --- 28 | 29 | Just run rtl_tcp (which comes with librtlsdr). Since the default auto-gain 30 | setting doesn't always result in the best SNR, you might want to play with the 31 | gain option [-g]. 32 | When rtl_tcp is running, start the DAB application. 33 | 34 | Settings 35 | --- 36 | 37 | By default, rtltcpaccess tries to connect to localhost:1234, you can change 38 | this by creating a key in the Wine registry: 39 | 40 | wine regedit 41 | 42 | Those are the names of the registry keys: 43 | 44 | [HKEY_CURRENT_USER\Software\rtltcpaccess] 45 | "address"="192.168.1.1" 46 | "port"=dword:00009999 47 | "testmode"=dword:00000000 48 | 49 | address is a string, port is a DWORD. If the testmode is enabled it checks for 50 | lost samples, just like rtl_test. 51 | 52 | Credits 53 | --- 54 | 55 | rtltcpaccess was written by Steve Markgraf and is 56 | released under the MIT License (Expat). 57 | -------------------------------------------------------------------------------- /dab_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Installation script for DAB receiver in Wine 3 | # https://github.com/steve-m/rtltcpaccess 4 | 5 | dab_md5="a25b2303badbea40df5e2e2ff58486a3" 6 | tcpacc_md5="73a6f0eab866340b7a8389e95c77e757" 7 | 8 | get_files() 9 | { 10 | if [ "`md5sum DABPlayer5.01.zip | cut -d ' ' -f 1`" != "$dab_md5" ]; then 11 | wget https://www.noxonradio.ch/download/NOXON_DAB_Stick/Updates/DABPlayer5.01.zip -O DABPlayer5.01.zip 12 | [ "`md5sum DABPlayer5.01.zip | cut -d ' ' -f 1`" != "$dab_md5" ] && return 1 13 | fi 14 | 15 | if [ "`md5sum rtltcpaccess.tar.gz | cut -d ' ' -f 1`" != "$tcpacc_md5" ]; then 16 | wget https://raw.github.com/steve-m/rtltcpaccess/master/rtltcpaccess.tar.gz -O rtltcpaccess.tar.gz 17 | [ "`md5sum rtltcpaccess.tar.gz | cut -d ' ' -f 1`" != "$tcpacc_md5" ] && return 2 18 | fi 19 | 20 | return 0 21 | } 22 | 23 | wine --version 24 | if [ "$?" != "0" ]; then 25 | echo "Wine needs to be installed!" 26 | exit 1 27 | fi 28 | 29 | # figure out application path 30 | app_path="`wine cmd /c echo %ProgramFiles% | tr -d '\r\n'`\NOXON\DAB Media Player" 31 | unix_path="`winepath -u "$app_path"`" 32 | 33 | mkdir /tmp/dab_install 34 | cd /tmp/dab_install/ 35 | 36 | # grab the software 37 | get_files 38 | if [ "$?" != "0" ]; then 39 | echo "Error fetching files, aborting!" 40 | exit 1 41 | fi 42 | 43 | # unpack the archive 44 | unzip -o DABPlayer5.01.zip 45 | 46 | # install it silently 47 | wine msiexec /i DABStickInstaller5.01.msi /qn 48 | 49 | rm "$unix_path/RTL283XACCESS.dll" 50 | 51 | tar xvf rtltcpaccess.tar.gz 52 | cp RTL283XACCESS.dll "$unix_path/" 53 | 54 | # Add device information to registry to make it believe a device is connected via USB 55 | wine regedit device_key.reg 56 | 57 | # Disable sending of usage statistics by setting registry variables 58 | # Normally there appears a dialog box that asks whether to disable this 59 | # when first starting the application, but starting with version 5 this 60 | # seems to trigger a Wine bug where the dialog box can't be closed. 61 | 62 | echo "REGEDIT4\n" > dab_settings.reg 63 | echo "[HKEY_CURRENT_USER\Software\Fraunhofer IIS\MultimediaPlayer\Settings\App]" >> dab_settings.reg 64 | echo "\"homecallActive\"=\"false\"" >> dab_settings.reg 65 | echo "\"homecallConfirmed\"=\"true\"" >> dab_settings.reg 66 | 67 | wine regedit dab_settings.reg 68 | 69 | echo "\nInstallation finished!\nTo run the software, start rtl_tcp first, and then start:" 70 | echo "wine \"\`winepath -u \"$app_path\NOXON_DAB_MediaPlayer.exe\"\`"\" 71 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * rtltcpaccess, a drop-in replacement for RTL283XACCESS.dll 3 | * https://github.com/steve-m/rtltcpaccess 4 | * 5 | * Copyright (c) 2013 Steve Markgraf 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included 16 | * in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define DEFAULT_IP "127.0.0.1" 34 | #define DEFAULT_PORT 1234 35 | #define BUFF_SIZE 24064 36 | 37 | static uint8_t buff[BUFF_SIZE]; 38 | static HANDLE got_data = NULL; 39 | static HANDLE pulled_data = NULL; 40 | static HANDLE thread_finished = NULL; 41 | static int exit_thread = FALSE; 42 | static int enable_testmode = FALSE; 43 | static SOCKET sock; 44 | 45 | #ifndef __WINE__ 46 | #pragma pack(push, 1) 47 | #else 48 | #include 49 | #endif 50 | struct command { 51 | unsigned char cmd; 52 | unsigned int param; 53 | }__attribute__((packed)); 54 | #ifndef __WINE__ 55 | #pragma pack(pop) 56 | #else 57 | #include 58 | #endif 59 | 60 | static int is_error(int perr) 61 | { 62 | /* Compare error to posix error code; return nonzero if match. */ 63 | #ifndef ENOPROTOOPT 64 | #define ENOPROTOOPT 109 65 | #endif 66 | /* All codes to be checked for must be defined below */ 67 | int werr = WSAGetLastError(); 68 | switch (werr) { 69 | case WSAETIMEDOUT: 70 | return(perr == EAGAIN); 71 | case WSAENOPROTOOPT: 72 | return(perr == ENOPROTOOPT); 73 | default: 74 | fprintf(stderr, "tcp: unknown error %d WS err %d\n", perr, werr); 75 | return 0; 76 | } 77 | } 78 | 79 | static void tcp_thread(void *param) 80 | { 81 | int received_bytes, bytes_left, index; 82 | 83 | while (!exit_thread) { 84 | bytes_left = BUFF_SIZE; 85 | index = 0; 86 | 87 | while (bytes_left > 0) { 88 | received_bytes = recv(sock, (char *)&buff[index], bytes_left, 0); 89 | 90 | if (received_bytes == -1 && !is_error(EAGAIN)) { 91 | fprintf(stderr, "[rtltcpaccess] socket error\n"); 92 | goto endthread; 93 | } 94 | 95 | bytes_left -= received_bytes; 96 | index += received_bytes; 97 | } 98 | 99 | if (got_data) { 100 | ResetEvent(pulled_data); 101 | SetEvent(got_data); 102 | WaitForSingleObject(pulled_data, INFINITE); 103 | } 104 | } 105 | 106 | endthread: 107 | SetEvent(thread_finished); 108 | _endthread(); 109 | } 110 | 111 | static BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) 112 | { 113 | switch(reason) { 114 | case DLL_PROCESS_ATTACH: 115 | DisableThreadLibraryCalls(hinst); 116 | break; 117 | case DLL_PROCESS_DETACH: 118 | break; 119 | } 120 | return TRUE; 121 | } 122 | 123 | DWORD RTK_BDAFilterInit(HANDLE hnd) 124 | { 125 | HKEY key; 126 | DWORD r, len = 0; 127 | const char settings_key[] = "Software\\rtltcpaccess"; 128 | char ip_addr[16] = DEFAULT_IP; 129 | uint32_t port = DEFAULT_PORT; 130 | uint32_t testmode = 0; 131 | int flag = 1, tries = 0; 132 | struct sockaddr_in remote; 133 | struct command samprate_cmd = { 0x02, htonl(2048000) }; 134 | struct command testmode_cmd = { 0x07, htonl(1) }; 135 | WSADATA wsd; 136 | 137 | r = WSAStartup(MAKEWORD(2,2), &wsd); 138 | 139 | if (!RegOpenKeyExA(HKEY_CURRENT_USER, settings_key, 0, KEY_READ, &key)) { 140 | r = RegQueryValueExA(key, "address", NULL, NULL, NULL, &len); 141 | 142 | if (!r && len > 7 && len <= sizeof(ip_addr)) 143 | r = RegQueryValueExA(key, "address", NULL, NULL, (BYTE *)ip_addr, &len); 144 | 145 | len = sizeof(port); 146 | r = RegQueryValueExA(key, "port", NULL, NULL, (BYTE *)&port, &len); 147 | 148 | if (r || port > 0xffff) 149 | port = DEFAULT_PORT; 150 | 151 | len = sizeof(testmode); 152 | r = RegQueryValueExA(key, "testmode", NULL, NULL, (BYTE *)&testmode, &len); 153 | 154 | if (!r) 155 | enable_testmode = testmode ? TRUE : FALSE; 156 | } 157 | 158 | sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 159 | 160 | memset(&remote, 0, sizeof(remote)); 161 | remote.sin_family = AF_INET; 162 | remote.sin_port = htons(port); 163 | remote.sin_addr.s_addr = inet_addr(ip_addr); 164 | 165 | if (remote.sin_addr.s_addr == INADDR_NONE) 166 | remote.sin_addr.s_addr = inet_addr(DEFAULT_IP); 167 | 168 | while (connect(sock, (struct sockaddr *)&remote, sizeof(remote)) != 0) { 169 | fprintf(stderr, "[rtltcpaccess] trying to connect to %s:%d...\n", ip_addr, port); 170 | if (tries > 10) { 171 | fprintf(stderr, "[rtltcpaccess] timeout, aborting\n"); 172 | return FALSE; 173 | } 174 | 175 | Sleep(500); 176 | tries++; 177 | } 178 | 179 | fprintf(stderr, "[rtltcpaccess] connected!\n"); 180 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); 181 | 182 | send(sock, (const char*)&samprate_cmd, sizeof(samprate_cmd), 0); 183 | 184 | if (enable_testmode) { 185 | fprintf(stderr, "[rtltcpaccess] enabled testmode\n"); 186 | send(sock, (const char*)&testmode_cmd, sizeof(testmode_cmd), 0); 187 | } 188 | 189 | pulled_data = CreateEventA(0, 0, 0, 0); 190 | thread_finished = CreateEventA(0, 0, 0, 0); 191 | _beginthread(tcp_thread, 0, NULL); 192 | 193 | return TRUE; 194 | } 195 | 196 | DWORD RTK_BDAFilterRelease(HANDLE hnd) 197 | { 198 | exit_thread = TRUE; 199 | 200 | if (thread_finished) 201 | WaitForSingleObject(thread_finished, INFINITE); 202 | 203 | if (sock != -1) { 204 | closesocket(sock); 205 | sock = -1; 206 | } 207 | 208 | WSACleanup(); 209 | 210 | return TRUE; 211 | } 212 | 213 | DWORD RTK_Set_Frequency(DWORD freq) 214 | { 215 | uint32_t freq_hz = freq * 1000; 216 | 217 | struct command cmd = { 0x01, htonl(freq_hz) }; 218 | send(sock, (const char*)&cmd, sizeof(cmd), 0); 219 | 220 | return TRUE; 221 | } 222 | 223 | DWORD RTK_Set_Bandwidth(DWORD bw) { return TRUE; } 224 | DWORD RTK_DeviceUpdate(void) { return TRUE; } 225 | 226 | DWORD RTK_GetData(LPBYTE data, DWORD bufsize, LPDWORD getlen, LPDWORD discardlen) 227 | { 228 | static uint8_t bcnt, uninit = 1; 229 | int i, lost = 0; 230 | 231 | if (enable_testmode) { 232 | if (uninit) { 233 | bcnt = buff[0]; 234 | uninit = 0; 235 | } 236 | 237 | for (i = 0; i < BUFF_SIZE; i++) { 238 | if(bcnt != buff[i]) { 239 | lost += (buff[i] > bcnt) ? (buff[i] - bcnt) : (bcnt - buff[i]); 240 | bcnt = buff[i]; 241 | } 242 | bcnt++; 243 | } 244 | 245 | if (lost) 246 | fprintf(stderr, "[rtltcpaccess] lost at least %d bytes\n", lost); 247 | } 248 | 249 | if (bufsize >= BUFF_SIZE) { 250 | memcpy(data, buff, BUFF_SIZE); 251 | *getlen = BUFF_SIZE; 252 | *discardlen = 0; 253 | SetEvent(pulled_data); 254 | } 255 | 256 | return TRUE; 257 | } 258 | 259 | DWORD RTK_Demod_Byte_Read(INT page, INT reg, INT len, PBYTE val) { return TRUE; } 260 | DWORD RTK_Demod_Byte_Write(INT page, INT reg, INT len, PBYTE val) { return TRUE; } 261 | 262 | DWORD RTK_SetDABEventHandle(HANDLE *handle) 263 | { 264 | got_data = *handle; 265 | return TRUE; 266 | } 267 | 268 | DWORD RTK_ReleaseDABEventHandle(HANDLE *handle) 269 | { 270 | got_data = NULL; 271 | return TRUE; 272 | } 273 | 274 | DWORD RTK_Get_TunerType(LPDWORD tuner_type) 275 | { 276 | *tuner_type = 5; /* E4000, doesn't matter */ 277 | return TRUE; 278 | } 279 | 280 | DWORD RTK_SYS_Byte_Read(WORD addr, INT len, PBYTE val) 281 | { 282 | /* expected register contents */ 283 | const uint8_t sys_regs[] = { 0x00, 0x88, 0x09, 0xdc, 0x03 }; 284 | 285 | if (addr < sizeof(sys_regs)) { 286 | *val = sys_regs[addr]; 287 | return TRUE; 288 | } 289 | 290 | return FALSE; 291 | } 292 | 293 | DWORD RTK_SYS_Byte_Write(WORD addr, INT len, PBYTE val) { return TRUE; } 294 | -------------------------------------------------------------------------------- /rtl283xaccess.def: -------------------------------------------------------------------------------- 1 | LIBRARY RTL283XACCESS 2 | EXPORTS 3 | RTK_BDAFilterInit 4 | RTK_BDAFilterRelease 5 | RTK_Demod_Byte_Read 6 | RTK_Demod_Byte_Write 7 | RTK_DeviceUpdate 8 | RTK_GetData 9 | RTK_Get_TunerType 10 | RTK_ReleaseDABEventHandle 11 | RTK_SYS_Byte_Read 12 | RTK_SYS_Byte_Write 13 | RTK_SetDABEventHandle 14 | RTK_Set_Bandwidth 15 | RTK_Set_Frequency 16 | -------------------------------------------------------------------------------- /rtltcpaccess.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steve-m/rtltcpaccess/8e3116a30ec4dcedee2ee634a57424ff705afe58/rtltcpaccess.tar.gz --------------------------------------------------------------------------------