Update to 1.66+, revert USB lib to 3.4, fix USB/Serial issue in Android 13+

- Updated source code with Emu48 version 1.66+
- Update Revert usb-serial-for-android to version 3.4
- Fix an USB serial issue with Android 13+ (fix #23)
This commit is contained in:
dgis 2024-10-05 21:19:13 +02:00
parent 8025fe107d
commit 9f58423a93
45 changed files with 4112 additions and 4428 deletions

View file

@ -60,7 +60,7 @@ CHANGES
Version 2.8 (2024-xx-xx)
- Update the usb serial drivers usb-serial-for-android to version 3.7.3
- Updated source code with Emu48 version 1.66+.
Version 2.7 (2024-06-14)
@ -277,6 +277,7 @@ FAQ
TODO
- NDK 26 does not compile
- Adding a new KML command for Android only: CUSTOM_PIXEL_BORDER_ON 10001 / CUSTOM_PIXEL_BORDER_OFF 10002 / CUSTOM_PIXEL_BORDER_TOGGLE 10003
- Patching 49G to disable 10 min auto off causes a reset when touching the Recent button and coming back to the app (with a Flashcard loaded).
- Add an "haptic" feedback with a sound instead of a vibration (F. Giampietro).
- Add a Cancel button to the HP48 memory card creator dialog.

View file

@ -65,7 +65,7 @@ add_library( # Sets the name of the library.
src/main/cpp/core/disrpl.c
# src/main/cpp/core/Emu48.c #-> To rewrite
src/main/cpp/core/engine.c
src/main/cpp/core/external.c
# src/main/cpp/core/external.c # Removed 167/166+
src/main/cpp/core/fetch.c
src/main/cpp/core/files.c
src/main/cpp/core/i28f160.c

View file

@ -34,8 +34,8 @@ android {
applicationId "org.emulator.forty.eight"
minSdk 21
targetSdk 34
versionCode 27
versionName "2.8"
versionCode 26
versionName "2.7"
setProperty("archivesBaseName", "Emu48-v$versionName")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {

View file

@ -16,7 +16,6 @@
android:fullBackupContent="@xml/backup_descriptor">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar"

View file

@ -526,10 +526,10 @@ h3 { color:red; font-size:1.1em; }
&quot;String&quot; object in stack level 1 to the clipboard. On all
other objects, the command will be ignored. This prevents sending
binary objects to the clipboard.</p>
<p>The decimal point (radix mark) of &quot;Real Numbers&quot; in the
clipboard is equal to the calculator setting. This is important when
you try to paste the numbers into a program using the locale settings
of the host operating system.</p>
<p>The decimal point of &quot;Real Numbers&quot; in the clipboard is
equal to the calculator setting. This is important when you try to
paste the numbers into a program using the locale settings of the host
operating system.</p>
<h2><a name=ss9.5>9.5 Paste Stack</a></h2>
<p>This menu item is enabled for the HP48SX, HP48GX and the HP49G
emulation.</p>
@ -539,14 +539,18 @@ h3 { color:red; font-size:1.1em; }
object. Is the content a complex number object, the number will be
saved as &quot;Complex Number&quot; object, otherwise cases as
&quot;String&quot; object.</p>
<p>To import &quot;Real or Complex Numbers&quot; from the clipboard, the
decimal point (radix mark) of the clipboard and calculator <u>must</u>
match. A real or complex number is only detected in the case of valid
real number characters in the clipboard. Especially heading and tailing
white spaces aren't valid number characters also.</p>
<p>Complex numbers must be in the form <i>(a,b)</i> when using the point
radix mark or in the form <i>(a;b)</i> when using the comma radix mark.
The Cartesian or algebraic form <i>a+bi</i> is not supported.</p>
<p>To import &quot;Real Numbers&quot; from the clipboard, the decimal
point character of the clipboard and calculator <u>must not match</u> any
more. There's an auto detection for decoding the thousands separator and
decimal point character. The thousands separator is removed at decoding.
A real number is detected in the case of valid real number characters in
the clipboard.</p>
<p>&quot;Complex Numbers&quot; must be in the form <i>(a,b)</i> when
using the decimal point or in the form <i>(a;b)</i> when using the decimal
comma. Using a thousands separator is not allowed in complex number
strings because in decimal point mode, the comma is used as separator
between real and imaginary part of the number. The Cartesian or algebraic
form <i>a+bi</i> is not supported.</p>
<h2><a name=ss9.6>9.6 Reset Calculator</a></h2>
<p>This emulates the Reset pin of the internal CPU.</p>
<h2><a name=ss9.7>9.7 Backup</a></h2>
@ -675,7 +679,7 @@ h3 { color:red; font-size:1.1em; }
Cyrille de Brebisson of Hewlett-Packard.</p>
<h1><a name=s15>15. License</a></h1>
<p>Emu48 - A HP38G/39G/40G/48SX/48GX/49G Emulator<br>
Copyright (C) 2022 Christoph Gie&szlig;elink</p>
Copyright (C) 2024 Christoph Gie&szlig;elink</p>
<p>This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)

View file

@ -58,11 +58,6 @@ LINKS
CHANGES
Version 2.8 (2024-xx-xx)
- Update the usb serial drivers usb-serial-for-android to version 3.7.3
Version 2.7 (2024-06-14)
- Updated source code with Emu48 version 1.65+. This new version improve the serial communication.

View file

@ -13,7 +13,7 @@
#include "kml.h"
#include "debugger.h"
#define VERSION "1.65+"
#define VERSION "1.66+"
#ifdef _DEBUG
LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug");
@ -2045,6 +2045,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
MSG msg;
WNDCLASS wc;
ATOM classAtom;
WSADATA wsd;
RECT rectWindow;
HACCEL hAccel;
DWORD dwThreadId;
@ -2059,16 +2060,8 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
hApp = hInst;
#if defined _UNICODE
{
ppArgv = (LPCTSTR*) CommandLineToArgvW(GetCommandLine(),&nArgc);
}
#else
{
nArgc = __argc; // no. of command line arguments
ppArgv = (LPCTSTR*) __argv; // command line arguments
}
#endif
nArgc = __argc; // no. of command line arguments
ppArgv = __targv; // command line arguments
wc.style = CS_BYTEALIGNCLIENT;
wc.lpfnWndProc = (WNDPROC)MainWndProc;
@ -2298,6 +2291,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
if (NewDocument()) SetWindowTitle(_T("Untitled"));
start:
VERIFY(WSAStartup(MAKEWORD(1,1),&wsd) == 0);
if (bStartupBackup) SaveBackup(); // make a RAM backup at startup
if (pbyRom) SwitchToState(SM_RUN);
@ -2313,6 +2307,7 @@ start:
DispatchMessage(&msg);
}
}
WSACleanup(); // cleanup network stack
// clean up DDE server
DdeNameService(idDdeInst, hszService, NULL, DNS_UNREGISTER);

View file

@ -369,6 +369,7 @@ extern BOOL SendByteUdp(BYTE byData);
// Stack.c
extern BOOL bDetectClpObject;
extern BOOL bLocaleDecimalPoint;
extern LRESULT OnStackCopy(VOID);
extern LRESULT OnStackPaste(VOID);

View file

@ -65,8 +65,8 @@ static CONST TCHAR cHex[] = { _T('0'),_T('1'),_T('2'),_T('3'),
_T('8'),_T('9'),_T('A'),_T('B'),
_T('C'),_T('D'),_T('E'),_T('F') };
static INT nDbgPosX = 0; // position of debugger window
static INT nDbgPosY = 0;
static INT nDbgPosX = CW_USEDEFAULT; // position of debugger window
static INT nDbgPosY = CW_USEDEFAULT;
static WORD wBreakpointCount = 0; // number of breakpoints
static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table
@ -1885,7 +1885,10 @@ static INT_PTR CALLBACK Debugger(HWND hDlg, UINT message, WPARAM wParam, LPARAM
switch (message)
{
case WM_INITDIALOG:
if (nDbgPosX != CW_USEDEFAULT) // not default window position
{
SetWindowLocation(hDlg,nDbgPosX,nDbgPosY);
}
if (bAlwaysOnTop) SetWindowPos(hDlg,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
SendMessage(hDlg,WM_SETICON,ICON_BIG,(LPARAM) LoadIcon(hApp,MAKEINTRESOURCE(IDI_EMU48)));
@ -2201,7 +2204,7 @@ static INT_PTR CALLBACK Debugger(HWND hDlg, UINT message, WPARAM wParam, LPARAM
LRESULT OnToolDebug(VOID) // debugger dialogbox call
{
if ((hDlgDebug = CreateDialog(hApp,MAKEINTRESOURCE(IDD_DEBUG),NULL,
if ((hDlgDebug = CreateDialog(hApp,MAKEINTRESOURCE(IDD_DEBUG),hWnd,
(DLGPROC)Debugger)) == NULL)
AbortMessage(_T("Debugger Dialog Box Creation Error !"));
return 0;
@ -3501,46 +3504,79 @@ static BOOL OnBrowseSaveMem(HWND hDlg)
//
// write file to memory
//
static BOOL LoadMemData(LPCTSTR lpszFilename,DWORD dwStartAddr)
static BOOL LoadMemData(LPCTSTR lpszFilename,DWORD dwStartAddr,UINT uBitMode)
{
HANDLE hFile;
DWORD dwRead;
BYTE byData;
DWORD dwFileSize,dwRead;
LPBYTE pbyData;
hFile = CreateFile(lpszFilename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file
return FALSE;
while (dwStartAddr <= 0xFFFFF) // read until EOF or end of Saturn address space
dwFileSize = GetFileSize(hFile, NULL);
if ((pbyData = (LPBYTE) malloc(dwFileSize)) != NULL)
{
ReadFile(hFile,&byData,sizeof(byData),&dwRead,NULL);
if (dwRead == 0) break; // EOF
if (dwStartAddr < 0xFFFFF)
ReadFile(hFile,pbyData,dwFileSize,&dwRead,NULL);
if (uBitMode == 2) // auto mode (0=8-bit, 1=4-bit, 2=auto)
{
Write2(dwStartAddr,byData); // write byte in map mode
dwStartAddr += 2;
BOOL bPacked = FALSE; // data not packed
DWORD dwIndex;
for (dwIndex = 0; !bPacked && dwIndex < dwFileSize; ++dwIndex)
{
bPacked = ((pbyData[dwIndex] & 0xF0) != 0);
}
uBitMode = bPacked ? 0 : 1; // 0=8-bit, 1=4-bit
}
if (uBitMode == 0) // 0=8-bit
{
LPBYTE pbyDataNew = (LPBYTE) realloc(pbyData,2*dwFileSize);
if (pbyDataNew)
{
LPBYTE pbySrc,pbyDest;
pbyData = pbyDataNew;
pbySrc = pbyData + dwFileSize;
dwFileSize *= 2; // new filesize
pbyDest = pbyData + dwFileSize;
while (pbySrc != pbyDest) // unpack source
{
CONST BYTE byValue = *(--pbySrc);
*(--pbyDest) = byValue >> 4;
*(--pbyDest) = byValue & 0xF;
}
_ASSERT(pbySrc == pbyData);
_ASSERT(pbyDest == pbyData);
}
else // special handling to avoid address wrap around
{
byData &= 0xF;
Nwrite(&byData,dwStartAddr,1); // write nibble in map mode
++dwStartAddr;
free(pbyData);
pbyData = NULL;
}
}
if (pbyData) // have data to save
{
LPBYTE p = pbyData;
while (dwFileSize > 0 && dwStartAddr <= 0xFFFFF)
{
Nwrite(p++,dwStartAddr++,1);
--dwFileSize;
}
}
free(pbyData);
}
CloseHandle(hFile);
return TRUE;
return pbyData != NULL;
}
//
// write memory data to file
//
static BOOL SaveMemData(LPCTSTR lpszFilename,DWORD dwStartAddr,DWORD dwEndAddr)
static BOOL SaveMemData(LPCTSTR lpszFilename,DWORD dwStartAddr,DWORD dwEndAddr,UINT uBitMode)
{
HANDLE hFile;
DWORD dwAddr,dwWritten;
BYTE byData;
DWORD dwAddr,dwSend,dwWritten;
BYTE byData[2];
hFile = CreateFile(lpszFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file
@ -3549,8 +3585,14 @@ static BOOL SaveMemData(LPCTSTR lpszFilename,DWORD dwStartAddr,DWORD dwEndAddr)
for (dwAddr = dwStartAddr; dwAddr <= dwEndAddr; dwAddr += 2)
{
_ASSERT(dwAddr <= 0xFFFFF);
byData = Read2(dwAddr); // read byte in map mode
WriteFile(hFile,&byData,sizeof(byData),&dwWritten,NULL);
Npeek(byData,dwAddr,2); // read two nibble in map mode
dwSend = 2; // send 2 nibble
if (uBitMode == 0) // (0=8-bit, 1=4-bit)
{
byData[0] = byData[0]|(byData[1]<<4);
dwSend = 1; // send 1 byte
}
WriteFile(hFile,&byData,dwSend,&dwWritten,NULL);
}
CloseHandle(hFile);
@ -3564,9 +3606,14 @@ static INT_PTR CALLBACK DebugMemLoad(HWND hDlg, UINT message, WPARAM wParam, LPA
{
TCHAR szFilename[MAX_PATH];
DWORD dwStartAddr;
int nButton;
UINT uBitMode;
switch (message)
{
case WM_INITDIALOG:
CheckDlgButton(hDlg,IDC_DEBUG_DATA_LOAD_ABIT,BST_CHECKED);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
@ -3586,8 +3633,14 @@ static INT_PTR CALLBACK DebugMemLoad(HWND hDlg, UINT message, WPARAM wParam, LPA
_ASSERT(dwStartAddr <= 0xFFFFF);
for (nButton = IDC_DEBUG_DATA_LOAD_8BIT; nButton <= IDC_DEBUG_DATA_LOAD_ABIT; ++nButton)
{
if (IsDlgButtonChecked(hDlg,nButton) == BST_CHECKED)
break;
}
uBitMode = (UINT) (nButton - IDC_DEBUG_DATA_LOAD_8BIT);
// load memory dump file
if (!LoadMemData(szFilename,dwStartAddr))
if (!LoadMemData(szFilename,dwStartAddr,uBitMode))
return FALSE;
// update memory window
@ -3617,9 +3670,13 @@ static INT_PTR CALLBACK DebugMemSave(HWND hDlg, UINT message, WPARAM wParam, LPA
{
TCHAR szFilename[MAX_PATH];
DWORD dwStartAddr,dwEndAddr;
UINT uBitMode;
switch (message)
{
case WM_INITDIALOG:
CheckDlgButton(hDlg,IDC_DEBUG_DATA_SAVE_8BIT,BST_CHECKED);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
@ -3643,8 +3700,9 @@ static INT_PTR CALLBACK DebugMemSave(HWND hDlg, UINT message, WPARAM wParam, LPA
_ASSERT(dwStartAddr <= 0xFFFFF);
_ASSERT(dwEndAddr <= 0xFFFFF);
uBitMode = IsDlgButtonChecked(hDlg,IDC_DEBUG_DATA_SAVE_4BIT);
// save memory dump file
if (!SaveMemData(szFilename,dwStartAddr,dwEndAddr))
if (!SaveMemData(szFilename,dwStartAddr,dwEndAddr,uBitMode))
return FALSE;
// no break

View file

@ -1,101 +0,0 @@
/*
* external.c
*
* This file is part of Emu48
*
* Copyright (C) 1995 Sebastien Carlier
*
*/
#include "pch.h"
#include "Emu48.h"
#include "ops.h"
#define SAMPLES_PER_SEC 44100 // sound sampling rate
//| 38G | 39G | 40G | 48SX | 48GX | 49G | Name
//#F0E4F #80F0F #80F0F #706D2 #80850 #80F0F =SFLAG53_56
// memory address for flags -53 to -56
// CdB for HP: add apples beep management
#define SFLAG53_56 ( (cCurrentRomType=='6') \
? 0xE0E4F \
: ( (cCurrentRomType=='A') \
? 0xF0E4F \
: ( (cCurrentRomType!='E' && cCurrentRomType!='X' && cCurrentRomType!='P' && cCurrentRomType!='2' && cCurrentRomType!='Q') \
? ( (cCurrentRomType=='S') \
? 0x706D2 \
: 0x80850 \
) \
: 0x80F0F \
) \
) \
)
VOID External(CHIPSET* w) // Beep patch
{
BYTE fbeep;
DWORD freq,dur;
freq = Npack(w->D,5); // frequency in Hz
dur = Npack(w->C,5); // duration in ms
Nread(&fbeep,SFLAG53_56,1); // fetch system flags -53 to -56
w->carry = TRUE; // setting of no beep
if (!(fbeep & 0x8) && freq) // bit -56 clear and frequency > 0 Hz
{
if (freq > 4400) freq = 4400; // high limit of HP (SX)
SoundBeep(freq,dur); // beeping
// estimate cpu cycles for beeping time (2MHz / 4MHz)
w->cycles += dur * ((cCurrentRomType=='S') ? 2000 : 4000);
// original routine return with...
w->P = 0; // P=0
w->intk = TRUE; // INTON
w->carry = FALSE; // RTNCC
}
w->pc = rstkpop();
return;
}
VOID RCKBp(CHIPSET* w) // ROM Check Beep patch
{
DWORD dw2F,dwCpuFreq;
DWORD freq,dur;
BYTE f,d;
f = w->C[1]; // f = freq ctl
d = w->C[0]; // d = duration ctl
if (cCurrentRomType == 'S') // Clarke chip with 48S ROM
{
// CPU strobe frequency @ RATE 14 = 1.97MHz
dwCpuFreq = ((14 + 1) * 524288) >> 2;
dw2F = f * 126 + 262; // F=f*63+131
}
else // York chip with 48G and later ROM
{
// CPU strobe frequency @ RATE 27 = 3.67MHz
// CPU strobe frequency @ RATE 29 = 3.93MHz
dwCpuFreq = ((27 + 1) * 524288) >> 2;
dw2F = f * 180 + 367; // F=f*90+183.5
}
freq = dwCpuFreq / dw2F;
dur = (dw2F * (256 - 16 * d)) * 1000 / 2 / dwCpuFreq;
if (freq > 4400) freq = 4400; // high limit of HP
SoundBeep(freq,dur); // beeping
// estimate cpu cycles for beeping time (2MHz / 4MHz)
w->cycles += dur * ((cCurrentRomType=='S') ? 2000 : 4000);
w->P = 0; // P=0
w->carry = FALSE; // RTNCC
w->pc = rstkpop();
return;
}

File diff suppressed because it is too large Load diff

View file

@ -664,7 +664,7 @@ which is possible in case of only 0 or 1 present symbols. */
static unsigned HuffmanTree_makeTable(HuffmanTree* tree) {
static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/
static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u;
unsigned i, numpresent, pointer, size; /*total table size*/
size_t i, numpresent, pointer, size; /*total table size*/
unsigned* maxlens = (unsigned*)lodepng_malloc(headsize * sizeof(unsigned));
if(!maxlens) return 83; /*alloc fail*/
@ -683,7 +683,7 @@ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) {
size = headsize;
for(i = 0; i < headsize; ++i) {
unsigned l = maxlens[i];
if(l > FIRSTBITS) size += (1u << (l - FIRSTBITS));
if(l > FIRSTBITS) size += (((size_t)1) << (l - FIRSTBITS));
}
tree->table_len = (unsigned char*)lodepng_malloc(size * sizeof(*tree->table_len));
tree->table_value = (unsigned short*)lodepng_malloc(size * sizeof(*tree->table_value));
@ -701,8 +701,8 @@ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) {
unsigned l = maxlens[i];
if(l <= FIRSTBITS) continue;
tree->table_len[i] = l;
tree->table_value[i] = pointer;
pointer += (1u << (l - FIRSTBITS));
tree->table_value[i] = (unsigned short)pointer;
pointer += (((size_t)1) << (l - FIRSTBITS));
}
lodepng_free(maxlens);
@ -726,7 +726,7 @@ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) {
unsigned index = reverse | (j << l);
if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
tree->table_len[index] = l;
tree->table_value[index] = i;
tree->table_value[index] = (unsigned short)i;
}
} else {
/*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/
@ -743,7 +743,7 @@ static unsigned HuffmanTree_makeTable(HuffmanTree* tree) {
unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */
unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS)));
tree->table_len[index2] = l;
tree->table_value[index2] = i;
tree->table_value[index2] = (unsigned short)i;
}
}
}
@ -2886,7 +2886,7 @@ static unsigned lodepng_chunk_createv(ucvector* out,
}
unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize,
unsigned length, const char* type, const unsigned char* data) {
size_t length, const char* type, const unsigned char* data) {
ucvector v = ucvector_init(*out, *outsize);
unsigned error = lodepng_chunk_createv(&v, length, type, data);
*out = v.data;
@ -3363,7 +3363,7 @@ unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) {
unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/
/*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/
unsigned p = (unsigned)index & m;
unsigned p = (unsigned) index & m;
in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/
in = in << (bits * (m - p));
if(p == 0) out[index * bits / 8u] = in;
@ -5889,7 +5889,7 @@ static size_t ilog2i(size_t i) {
l = ilog2(i);
/* approximate i*log2(i): l is integer logarithm, ((i - (1u << l)) << 1u)
linearly approximates the missing fractional part multiplied by i */
return i * l + ((i - ((size_t) 1u << l)) << 1u);
return i * l + ((i - (((size_t)1) << l)) << 1u);
}
static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h,

View file

@ -1001,7 +1001,7 @@ and data separately. The type is a 4-letter string.
The out variable and outsize are updated to reflect the new reallocated buffer.
Returne error code (0 if it went ok)
*/
unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, size_t length,
const char* type, const unsigned char* data);

View file

@ -1002,9 +1002,9 @@ static DWORD ReadT2Acc(VOID)
// maybe CPU speed measurement, slow down the next 10 CPU opcodes
if (dwCycDif < 150)
{
InitAdjustSpeed(); // init variables if necessary
EnterCriticalSection(&csSlowLock);
{
InitAdjustSpeed(); // init variables if necessary
nOpcSlow = 10; // slow down next 10 opcodes
}
LeaveCriticalSection(&csSlowLock);
@ -1146,9 +1146,9 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
// CPU running with max. speed we may get a timeout overflow
// -> to avoid this slow down CPU speed on transmit buffer full
InitAdjustSpeed(); // init variables if necessary
EnterCriticalSection(&csSlowLock);
{
InitAdjustSpeed(); // init variables if necessary
nOpcSlow = 10; // slow down next 10 opcodes
}
LeaveCriticalSection(&csSlowLock);

View file

@ -121,62 +121,67 @@
#define IDC_DEBUG_DATA_BUT 1084
#define IDC_DEBUG_DATA_STARTADDR 1085
#define IDC_DEBUG_DATA_ENDADDR 1086
#define IDC_DEBUG_SET_SYMB 1087
#define IDC_DEBUG_SET_MODEL 1088
#define IDC_DEBUG_SET_FILE 1089
#define IDC_DEBUG_SET_BROWSE 1090
#define IDC_DEBUG_STACK 1091
#define IDC_STATIC_BREAKPOINT 1092
#define IDC_BREAKEDIT_ADD 1093
#define IDC_BREAKEDIT_DELETE 1094
#define IDC_BREAKEDIT_WND 1095
#define IDC_STATIC_MMU 1096
#define IDC_MMU_IO_A 1097
#define IDC_MMU_NCE2_A 1098
#define IDC_MMU_CE1_A 1099
#define IDC_MMU_CE2_A 1100
#define IDC_MMU_NCE3_A 1101
#define IDC_MMU_IO_S 1102
#define IDC_MMU_CE1_S 1103
#define IDC_MMU_CE2_S 1104
#define IDC_MMU_NCE2_S 1105
#define IDC_MMU_NCE3_S 1106
#define IDC_STATIC_MISC 1107
#define IDC_MISC_BS_TXT 1108
#define IDC_INSTR_TEXT 1109
#define IDC_INSTR_CODE 1110
#define IDC_INSTR_COPY 1111
#define IDC_INSTR_CLEAR 1112
#define IDC_PROFILE_LASTCYCLES 1113
#define IDC_PROFILE_LASTTIME 1114
#define IDC_BPCODE 1115
#define IDC_BPRPL 1116
#define IDC_BPACCESS 1117
#define IDC_BPREAD 1118
#define IDC_BPWRITE 1119
#define IDC_FIND_DATA 1120
#define IDC_FIND_PREV 1121
#define IDC_FIND_NEXT 1122
#define IDC_FIND_ASCII 1123
#define IDC_ADDR20_24 1124
#define IDC_ADDR25_27 1125
#define IDC_ADDR28_29 1126
#define IDC_ADDR30_34 1127
#define IDC_RPLVIEW_DATA 1128
#define IDC_MACRO_SLOW 1129
#define IDC_MACRO_FAST 1130
#define IDC_MACRO_SLIDER 1131
#define IDC_MACRO_REAL 1132
#define IDC_MACRO_MANUAL 1133
#define IDC_SOUND_SLIDER 1134
#define IDC_SOUND_DEVICE 1135
#define IDC_TRACE_FILE 1136
#define IDC_TRACE_BROWSE 1137
#define IDC_TRACE_NEW 1138
#define IDC_TRACE_APPEND 1139
#define IDC_TRACE_REGISTER 1140
#define IDC_TRACE_MMU 1141
#define IDC_TRACE_OPCODE 1142
#define IDC_DEBUG_DATA_SAVE_8BIT 1087
#define IDC_DEBUG_DATA_SAVE_4BIT 1088
#define IDC_DEBUG_DATA_LOAD_8BIT 1089
#define IDC_DEBUG_DATA_LOAD_4BIT 1090
#define IDC_DEBUG_DATA_LOAD_ABIT 1091
#define IDC_DEBUG_SET_SYMB 1092
#define IDC_DEBUG_SET_MODEL 1093
#define IDC_DEBUG_SET_FILE 1094
#define IDC_DEBUG_SET_BROWSE 1095
#define IDC_DEBUG_STACK 1096
#define IDC_STATIC_BREAKPOINT 1097
#define IDC_BREAKEDIT_ADD 1098
#define IDC_BREAKEDIT_DELETE 1099
#define IDC_BREAKEDIT_WND 1100
#define IDC_STATIC_MMU 1101
#define IDC_MMU_IO_A 1102
#define IDC_MMU_NCE2_A 1103
#define IDC_MMU_CE1_A 1104
#define IDC_MMU_CE2_A 1105
#define IDC_MMU_NCE3_A 1106
#define IDC_MMU_IO_S 1107
#define IDC_MMU_CE1_S 1108
#define IDC_MMU_CE2_S 1109
#define IDC_MMU_NCE2_S 1110
#define IDC_MMU_NCE3_S 1111
#define IDC_STATIC_MISC 1112
#define IDC_MISC_BS_TXT 1113
#define IDC_INSTR_TEXT 1114
#define IDC_INSTR_CODE 1115
#define IDC_INSTR_COPY 1116
#define IDC_INSTR_CLEAR 1117
#define IDC_PROFILE_LASTCYCLES 1118
#define IDC_PROFILE_LASTTIME 1119
#define IDC_BPCODE 1120
#define IDC_BPRPL 1121
#define IDC_BPACCESS 1122
#define IDC_BPREAD 1123
#define IDC_BPWRITE 1124
#define IDC_FIND_DATA 1125
#define IDC_FIND_PREV 1126
#define IDC_FIND_NEXT 1127
#define IDC_FIND_ASCII 1128
#define IDC_ADDR20_24 1129
#define IDC_ADDR25_27 1130
#define IDC_ADDR28_29 1131
#define IDC_ADDR30_34 1132
#define IDC_RPLVIEW_DATA 1133
#define IDC_MACRO_SLOW 1134
#define IDC_MACRO_FAST 1135
#define IDC_MACRO_SLIDER 1136
#define IDC_MACRO_REAL 1137
#define IDC_MACRO_MANUAL 1138
#define IDC_SOUND_SLIDER 1139
#define IDC_SOUND_DEVICE 1140
#define IDC_TRACE_FILE 1141
#define IDC_TRACE_BROWSE 1142
#define IDC_TRACE_NEW 1143
#define IDC_TRACE_APPEND 1144
#define IDC_TRACE_REGISTER 1145
#define IDC_TRACE_MMU 1146
#define IDC_TRACE_OPCODE 1147
#define ID_FILE_NEW 40001
#define ID_FILE_OPEN 40002
#define ID_FILE_SAVE 40003
@ -257,7 +262,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 131
#define _APS_NEXT_COMMAND_VALUE 40074
#define _APS_NEXT_CONTROL_VALUE 1143
#define _APS_NEXT_CONTROL_VALUE 1148
#define _APS_NEXT_SYMED_VALUE 109
#endif
#endif

View file

@ -241,8 +241,8 @@ VOID RebuildRomCrc(VOID)
{
// has no Crc
}
// HP49G
if (cCurrentRomType == 'X' && dwRomSize == _KB(2048))
// HP48GII/49G+/50G/49G
if ((strchr("2QX",cCurrentRomType)) && dwRomSize == _KB(2048))
{
CorrectAllFlashPages(); // go through all pages
}

View file

@ -104,7 +104,6 @@ BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort)
LPCTSTR strPort = (Chipset.IORam[IR_CTRL] & EIRU) ? strIrPort : strWirePort;
_ASSERT(Chipset.IORam[IOC] & SON); // UART on
CommClose(); // close port if already open
dwBytesRead = 0L; // no bytes received

View file

@ -182,23 +182,24 @@ VOID ReadSettings(VOID)
disassembler_mode = ReadInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode);
disassembler_symb = ReadInt(_T("Disassembler"),_T("Symbolic"),disassembler_symb);
// Emulator
bShowTitle = ReadInt(_T("Emulator"),_T("ShowTitle"),bShowTitle);
bShowMenu = ReadInt(_T("Emulator"),_T("ShowMenu"),bShowMenu);
bAlwaysOnTop = ReadInt(_T("Emulator"),_T("AlwaysOnTop"),bAlwaysOnTop);
bActFollowsMouse = ReadInt(_T("Emulator"),_T("ActivationFollowsMouse"),bActFollowsMouse);
bClientWinMove = ReadInt(_T("Emulator"),_T("ClientWinMove"),bClientWinMove);
bSingleInstance = ReadInt(_T("Emulator"),_T("SingleInstance"),bSingleInstance);
bRealSpeed = ReadInt(_T("Emulator"),_T("RealSpeed"),bRealSpeed);
dwSXCycles = ReadInt(_T("Emulator"),_T("SXCycles"),dwSXCycles);
dwGXCycles = ReadInt(_T("Emulator"),_T("GXCycles"),dwGXCycles);
dwGPCycles = ReadInt(_T("Emulator"),_T("GPCycles"),dwGPCycles); // CdB for HP: add apples
dwG2Cycles = ReadInt(_T("Emulator"),_T("G2Cycles"),dwG2Cycles); // CdB for HP: add apples
dwKeyMinDelay = ReadInt(_T("Emulator"),_T("KeyMinDelay"),dwKeyMinDelay);
dwWakeupDelay = ReadInt(_T("Emulator"),_T("WakeupDelay"),dwWakeupDelay);
bGrayscale = ReadInt(_T("Emulator"),_T("Grayscale"),bGrayscale);
uWaveDevId = ReadInt(_T("Emulator"),_T("WaveDeviceId"),uWaveDevId);
dwWaveVol = ReadInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol);
dwWaveTime = ReadInt(_T("Emulator"),_T("WaveTime"),dwWaveTime);
bShowTitle = ReadInt(_T("Emulator"),_T("ShowTitle"),bShowTitle);
bShowMenu = ReadInt(_T("Emulator"),_T("ShowMenu"),bShowMenu);
bAlwaysOnTop = ReadInt(_T("Emulator"),_T("AlwaysOnTop"),bAlwaysOnTop);
bActFollowsMouse = ReadInt(_T("Emulator"),_T("ActivationFollowsMouse"),bActFollowsMouse);
bClientWinMove = ReadInt(_T("Emulator"),_T("ClientWinMove"),bClientWinMove);
bSingleInstance = ReadInt(_T("Emulator"),_T("SingleInstance"),bSingleInstance);
bRealSpeed = ReadInt(_T("Emulator"),_T("RealSpeed"),bRealSpeed);
dwSXCycles = ReadInt(_T("Emulator"),_T("SXCycles"),dwSXCycles);
dwGXCycles = ReadInt(_T("Emulator"),_T("GXCycles"),dwGXCycles);
dwGPCycles = ReadInt(_T("Emulator"),_T("GPCycles"),dwGPCycles); // CdB for HP: add apples
dwG2Cycles = ReadInt(_T("Emulator"),_T("G2Cycles"),dwG2Cycles); // CdB for HP: add apples
dwKeyMinDelay = ReadInt(_T("Emulator"),_T("KeyMinDelay"),dwKeyMinDelay);
dwWakeupDelay = ReadInt(_T("Emulator"),_T("WakeupDelay"),dwWakeupDelay);
bGrayscale = ReadInt(_T("Emulator"),_T("Grayscale"),bGrayscale);
uWaveDevId = ReadInt(_T("Emulator"),_T("WaveDeviceId"),uWaveDevId);
dwWaveVol = ReadInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol);
dwWaveTime = ReadInt(_T("Emulator"),_T("WaveTime"),dwWaveTime);
bLocaleDecimalPoint = ReadInt(_T("Emulator"),_T("LocaleDecimalPoint"),bLocaleDecimalPoint);
// LowBat
bLowBatDisable = ReadInt(_T("LowBat"),_T("Disable"),bLowBatDisable);
// Macro
@ -254,6 +255,7 @@ VOID WriteSettings(VOID)
WriteInt(_T("Emulator"),_T("WaveDeviceId"),uWaveDevId);
WriteInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol);
WriteInt(_T("Emulator"),_T("WaveTime"),dwWaveTime);
WriteInt(_T("Emulator"),_T("LocaleDecimalPoint"),bLocaleDecimalPoint);
// LowBat
WriteInt(_T("LowBat"),_T("Disable"),bLowBatDisable);
// Macro

View file

@ -19,6 +19,7 @@
#define DOCSTR 0x02A2C // String
BOOL bDetectClpObject = TRUE; // try to detect clipboard object
BOOL bLocaleDecimalPoint = FALSE; // use decimal point character from calculator
//################
//#
@ -26,6 +27,100 @@ BOOL bDetectClpObject = TRUE; // try to detect clipboard object
//#
//################
//
// check if cGroup character is thousands separator
//
static BOOL CheckThousandGroup(LPCTSTR cp,TCHAR cGroup)
{
UINT uLastPos;
UINT i;
// get decimal point
CONST TCHAR cDecimalPoint = cGroup ^ (_T('.') ^ _T(','));
BOOL bFound = FALSE; // 1st separator not found
BOOL bPosOK = TRUE;
for (i = 0; bPosOK && cp[i] != cDecimalPoint && cp[i] != 0; ++i)
{
if (cp[i] == cGroup) // found separator
{
if (bFound)
{
// verify separator position
bPosOK = (uLastPos + 4 == i);
}
uLastPos = i; // last position of separator
bFound = TRUE; // separator found
}
}
// check last grouping
return bPosOK && bFound && (uLastPos + 4 == i);
}
//
// get decimal point from clipboard
//
static TCHAR GetClpbrdDecimalPoint(LPCTSTR cp)
{
TCHAR cDec = 0; // default for invalid decimal point detection
TCHAR cLast = 0; // last decimal point
UINT uPoint = 0; // no. of points
UINT uComma = 0; // no. of commas
LPCTSTR p;
for (p = cp; *p; ++p) // count '.' and ',' characters
{
if (*p == _T('.'))
{
cLast = *p; // last occurance
++uPoint;
}
if (*p == _T(','))
{
cLast = *p; // last occurance
++uComma;
}
}
if (uComma == 0 && uPoint == 0) // none of both
{
cDec = _T('.');
}
else if (uComma == 1 && uPoint == 0) // one single ','
{
cDec = _T(',');
}
else if (uComma == 0 && uPoint == 1) // one single '.'
{
cDec = _T('.');
}
else if (uComma == 1 && uPoint == 1) // one single ',' and '.'
{
// change from ',' to '.' or vice versa
const TCHAR cFirst = cLast ^ (_T('.') ^ _T(','));
if (CheckThousandGroup(cp,cFirst)) // check if 1st character is grouped
{
cDec = cLast;
}
}
// multiple grouped ',' and single '.'
else if (uComma > 1 && uPoint == 1 && CheckThousandGroup(cp,_T(',')))
{
cDec = _T('.');
}
// multiple grouped '.' and single ','
else if (uComma == 1 && uPoint > 1 && CheckThousandGroup(cp,_T('.')))
{
cDec = _T(',');
}
return cDec;
}
static LPTSTR Trim(LPCTSTR cp)
{
LPCTSTR pcWs = _T(" \t\n\r"); // valid whitespace characters
@ -238,22 +333,23 @@ static INT RPL_GetBcd(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cD
static __inline INT SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize)
{
TCHAR cVc[] = _T(".0123456789eE+-");
TCHAR cVc[] = _T(",.0123456789eE+-");
BYTE byNum[80];
INT i,nIp,nDp,nMaxExp;
LONG lExp;
cVc[0] = cDec; // replace decimal char
// get thousand separator
const TCHAR cThousand = cDec ^ (_T('.') ^ _T(','));
if ( nMantLen + nExpLen >= nSize // destination buffer too small
|| !*cp // empty string
|| _tcsspn(cp,cVc) != (SIZE_T) lstrlen(cp) // real contain only these numbers
|| _tcsspn(cp,cVc) != (SIZE_T) lstrlen(cp) // real doesn't contain only these numbers
|| (SIZE_T) lstrlen(cp) >= ARRAYSIZEOF(byNum)) // ignore too long reals
return 0;
byNum[0] = (*cp != _T('-')) ? 0 : 9; // set sign nibble
if (*cp == _T('-') || *cp == _T('+')) // skip sign character
if (*cp == _T('-') || *cp == _T('+')) // skip sign nibble
cp++;
// only '.', '0' .. '9' are valid here
@ -264,8 +360,13 @@ static __inline INT SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,
if (*cp != cDec) // no decimal point
{
// count integer part
while (*cp >= _T('0') && *cp <= _T('9'))
byNum[++nIp] = *cp++ - _T('0');
for (; (*cp >= _T('0') && *cp <= _T('9')) || *cp == cThousand; ++cp)
{
if (*cp != cThousand) // not thousand separator
{
byNum[++nIp] = *cp - _T('0');
}
}
if (!nIp) return 0;
}
@ -376,8 +477,8 @@ static INT RPL_GetComplex(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHA
TCHAR cSep;
cSep = (cDec == _T('.')) // current separator
? _T(',') // radix mark '.' -> ',' separator
: _T(';'); // radix mark ',' -> ';' separator
? _T(',') // decimal point '.' -> ',' separator
: _T(';'); // decimal comma ',' -> ';' separator
nPos = 0; // write buffer position
@ -420,8 +521,8 @@ static INT RPL_SetComplex(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,L
nLen = 0; // read data length
cSep = (cDec == _T('.')) // current separator
? _T(',') // radix mark '.' -> ',' separator
: _T(';'); // radix mark ',' -> ';' separator
? _T(',') // decimal point '.' -> ',' separator
: _T(';'); // decimal comma ',' -> ';' separator
if ((pszData = Trim(cp)) != NULL) // create a trimmed working copy of the string
{
@ -468,8 +569,14 @@ static INT RPL_SetComplex(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,L
static TCHAR GetRadix(VOID)
{
// get locale decimal point
// GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,&cDecimal,1);
if (bLocaleDecimalPoint) // use Windows Locale decimal point character
{
TCHAR cDecimal[2];
// get locale decimal point with zero terminator
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,cDecimal,2);
return cDecimal[0];
}
return RPL_GetSystemFlag(fnRadix) ? _T(',') : _T('.');
}
@ -683,9 +790,11 @@ LRESULT OnStackPaste(VOID) // paste data to stack
if ((lpstrClipdata = (LPCTSTR) GlobalLock(hClipObj)))
{
TCHAR cDec;
BYTE byNumber[128];
DWORD dwAddress;
INT s;
INT s = 0; // no valid object
do
{
@ -715,9 +824,14 @@ LRESULT OnStackPaste(VOID) // paste data to stack
}
}
// try to convert string to real format
_ASSERT(16 <= ARRAYSIZEOF(byNumber));
s = RPL_SetBcd(lpstrClipdata,12,3,GetRadix(),byNumber,sizeof(byNumber));
cDec = GetClpbrdDecimalPoint(lpstrClipdata);
if (cDec) // valid decimal point
{
// try to convert string to real format
_ASSERT(16 <= ARRAYSIZEOF(byNumber));
s = RPL_SetBcd(lpstrClipdata,12,3,cDec,byNumber,sizeof(byNumber));
}
if (s > 0) // is a real number
{
@ -736,9 +850,14 @@ LRESULT OnStackPaste(VOID) // paste data to stack
break;
}
// search for ';' as separator
cDec = (_tcschr(lpstrClipdata,_T(';')) != NULL)
? _T(',') // decimal comma
: _T('.'); // decimal point
// try to convert string to complex format
_ASSERT(32 <= ARRAYSIZEOF(byNumber));
s = RPL_SetComplex(lpstrClipdata,12,3,GetRadix(),byNumber,sizeof(byNumber));
s = RPL_SetComplex(lpstrClipdata,12,3,cDec,byNumber,sizeof(byNumber));
if (s > 0) // is a real complex
{

View file

@ -247,7 +247,7 @@ VOID SetHP48Time(VOID) // set date and time
ULONGLONG ticks, time;
DWORD dw;
WORD crc, i;
BYTE p[4];
LPBYTE pbyTime;
_ASSERT(sizeof(ULONGLONG) == 8); // check size of datatype
@ -282,29 +282,23 @@ VOID SetHP48Time(VOID) // set date and time
time = ticks; // save for calc. timeout
time += OFF_TIME; // add 10 min for auto off
dw = RPLTIME; // HP addresses for clock in port0
pbyTime = Port0 + RPLTIME; // HP addresses for clock in port0
crc = 0x0; // reset crc value
for (i = 0; i < 13; ++i, ++dw) // write date and time
for (i = 0; i < 13; ++i) // write date and time
{
*p = (BYTE) ticks & 0xf;
crc = (crc >> 4) ^ (((crc ^ ((WORD) *p)) & 0xf) * 0x1081);
Port0[dw] = *p; // always store in port0
*pbyTime = (BYTE) ticks & 0xf; // time
crc = UpCRC(crc,*pbyTime);
ticks >>= 4;
}
Nunpack(p,crc,4); // write crc
memcpy(Port0+dw,p,4); // always store in port0
dw += 4; // HP addresses for timeout
for (i = 0; i < 13; ++i, ++dw) // write time for auto off
{
Port0[dw] = (BYTE) time & 0xf; // always store in port0
pbyTime[13+4] = (BYTE) time & 0xf; // auto off
time >>= 4;
++pbyTime;
}
Port0[dw] = 0xf; // always store in port0
Nunpack(pbyTime,crc,4); // write crc
pbyTime[13+4] = 0xf;
return;
}

View file

@ -22,13 +22,10 @@ VOID ResetUdp(VOID)
BOOL SendByteUdp(BYTE byData)
{
WSADATA wsd;
SOCKET sClient;
BOOL bErr = TRUE;
VERIFY(WSAStartup(MAKEWORD(1,1),&wsd) == 0);
// IP address not specified
if (sServer.sin_addr.s_addr == INADDR_NONE)
{
@ -72,7 +69,5 @@ BOOL SendByteUdp(BYTE byData)
bErr = sendto(sClient, (LPCCH) &byData, sizeof(byData), 0, (LPSOCKADDR) &sServer, sizeof(sServer)) == SOCKET_ERROR;
closesocket(sClient);
}
WSACleanup(); // cleanup network stack
return bErr;
}

File diff suppressed because it is too large Load diff

View file

@ -1199,9 +1199,12 @@ typedef struct tagCOPYDATASTRUCT {
PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
// Locale info
#define LOCALE_USER_DEFAULT 1024
#define LOCALE_SDECIMAL 0x0000000E // decimal separator, eg "." for 1,234.00
typedef DWORD LCID;
typedef DWORD LCTYPE;
extern int GetLocaleInfo(LCID Locale, LCTYPE LCType, LPSTR lpLCData, int cchData);
#ifdef UNICODE

View file

@ -158,7 +158,9 @@ public class Serial {
if(usbConnection == null && usbPermission == UsbPermission.Unknown && !usbManager.hasPermission(driver.getDevice())) {
usbPermission = UsbPermission.Requested;
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_MUTABLE : 0;
PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(INTENT_ACTION_GRANT_USB), flags);
Intent intent = new Intent(INTENT_ACTION_GRANT_USB);
intent.setPackage(context.getPackageName());
PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(context, 0, intent, flags);
usbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
if(debug) Log.d(TAG, "Request permission");
connectionStatus = "serial_connection_failed_user_has_not_given_permission";

View file

@ -8,13 +8,11 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import org.emulator.calculator.usbserial.util.HexDump;
import org.emulator.calculator.usbserial.util.UsbUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
@ -32,8 +30,6 @@ import java.util.Map;
*/
public class CdcAcmSerialDriver implements UsbSerialDriver {
public static final int USB_SUBCLASS_ACM = 2;
private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
private final UsbDevice mDevice;
@ -42,31 +38,21 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
public CdcAcmSerialDriver(UsbDevice device) {
mDevice = device;
mPorts = new ArrayList<>();
int ports = countPorts(device);
for (int port = 0; port < ports; port++) {
mPorts.add(new CdcAcmSerialPort(mDevice, port));
}
if (mPorts.size() == 0) {
mPorts.add(new CdcAcmSerialPort(mDevice, -1));
}
}
@SuppressWarnings({"unused"})
public static boolean probe(UsbDevice device) {
return countPorts(device) > 0;
}
private static int countPorts(UsbDevice device) {
int controlInterfaceCount = 0;
int dataInterfaceCount = 0;
for (int i = 0; i < device.getInterfaceCount(); i++) {
if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM &&
device.getInterface(i).getInterfaceSubclass() == USB_SUBCLASS_ACM)
for( int i = 0; i < device.getInterfaceCount(); i++) {
if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM)
controlInterfaceCount++;
if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
dataInterfaceCount++;
}
return Math.min(controlInterfaceCount, dataInterfaceCount);
for( int port = 0; port < Math.min(controlInterfaceCount, dataInterfaceCount); port++) {
mPorts.add(new CdcAcmSerialPort(mDevice, port));
}
if(mPorts.size() == 0) {
mPorts.add(new CdcAcmSerialPort(mDevice, -1));
}
}
@Override
@ -109,11 +95,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
}
@Override
protected void openInt() throws IOException {
Log.d(TAG, "interfaces:");
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
Log.d(TAG, mDevice.getInterface(i).toString());
}
protected void openInt(UsbDeviceConnection connection) throws IOException {
if (mPortNumber == -1) {
Log.d(TAG,"device might be castrated ACM device, trying single interface logic");
openSingleInterface();
@ -149,57 +131,38 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
}
private void openInterface() throws IOException {
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
int controlInterfaceCount = 0;
int dataInterfaceCount = 0;
mControlInterface = null;
mDataInterface = null;
int j = getInterfaceIdFromDescriptors();
Log.d(TAG, "interface count=" + mDevice.getInterfaceCount() + ", IAD=" + j);
if (j >= 0) {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbInterface = mDevice.getInterface(i);
if (usbInterface.getId() == j || usbInterface.getId() == j+1) {
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM &&
usbInterface.getInterfaceSubclass() == USB_SUBCLASS_ACM) {
mControlIndex = usbInterface.getId();
mControlInterface = usbInterface;
}
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
mDataInterface = usbInterface;
}
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbInterface = mDevice.getInterface(i);
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) {
if(controlInterfaceCount == mPortNumber) {
mControlIndex = i;
mControlInterface = usbInterface;
}
controlInterfaceCount++;
}
}
if (mControlInterface == null || mDataInterface == null) {
Log.d(TAG, "no IAD fallback");
int controlInterfaceCount = 0;
int dataInterfaceCount = 0;
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbInterface = mDevice.getInterface(i);
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM &&
usbInterface.getInterfaceSubclass() == USB_SUBCLASS_ACM) {
if (controlInterfaceCount == mPortNumber) {
mControlIndex = usbInterface.getId();
mControlInterface = usbInterface;
}
controlInterfaceCount++;
}
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
if (dataInterfaceCount == mPortNumber) {
mDataInterface = usbInterface;
}
dataInterfaceCount++;
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
if(dataInterfaceCount == mPortNumber) {
mDataInterface = usbInterface;
}
dataInterfaceCount++;
}
}
if(mControlInterface == null) {
throw new IOException("No control interface");
}
Log.d(TAG, "Control interface id " + mControlInterface.getId());
Log.d(TAG, "Control iface=" + mControlInterface);
if (!mConnection.claimInterface(mControlInterface, true)) {
throw new IOException("Could not claim control interface");
}
mControlEndpoint = mControlInterface.getEndpoint(0);
if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) {
throw new IOException("Invalid control endpoint");
@ -208,10 +171,12 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
if(mDataInterface == null) {
throw new IOException("No data interface");
}
Log.d(TAG, "data interface id " + mDataInterface.getId());
Log.d(TAG, "data iface=" + mDataInterface);
if (!mConnection.claimInterface(mDataInterface, true)) {
throw new IOException("Could not claim data interface");
}
for (int i = 0; i < mDataInterface.getEndpointCount(); i++) {
UsbEndpoint ep = mDataInterface.getEndpoint(i);
if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
@ -221,36 +186,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
}
}
private int getInterfaceIdFromDescriptors() {
ArrayList<byte[]> descriptors = UsbUtils.getDescriptors(mConnection);
Log.d(TAG, "USB descriptor:");
for(byte[] descriptor : descriptors)
Log.d(TAG, HexDump.toHexString(descriptor));
if (descriptors.size() > 0 &&
descriptors.get(0).length == 18 &&
descriptors.get(0)[1] == 1 && // bDescriptorType
descriptors.get(0)[4] == (byte)(UsbConstants.USB_CLASS_MISC) && //bDeviceClass
descriptors.get(0)[5] == 2 && // bDeviceSubClass
descriptors.get(0)[6] == 1) { // bDeviceProtocol
// is IAD device, see https://www.usb.org/sites/default/files/iadclasscode_r10.pdf
int port = -1;
for (int d = 1; d < descriptors.size(); d++) {
if (descriptors.get(d).length == 8 &&
descriptors.get(d)[1] == 0x0b && // bDescriptorType == IAD
descriptors.get(d)[4] == UsbConstants.USB_CLASS_COMM && // bFunctionClass == CDC
descriptors.get(d)[5] == USB_SUBCLASS_ACM) { // bFunctionSubClass == ACM
port++;
if (port == mPortNumber &&
descriptors.get(d)[3] == 2) { // bInterfaceCount
return descriptors.get(d)[2]; // bFirstInterface
}
}
}
}
return -1;
}
private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException {
int len = mConnection.controlTransfer(
USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000);
@ -351,9 +286,42 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
}
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() {
return new LinkedHashMap<>();
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_ARDUINO,
new int[] {
UsbId.ARDUINO_UNO,
UsbId.ARDUINO_UNO_R3,
UsbId.ARDUINO_MEGA_2560,
UsbId.ARDUINO_MEGA_2560_R3,
UsbId.ARDUINO_SERIAL_ADAPTER,
UsbId.ARDUINO_SERIAL_ADAPTER_R3,
UsbId.ARDUINO_MEGA_ADK,
UsbId.ARDUINO_MEGA_ADK_R3,
UsbId.ARDUINO_LEONARDO,
UsbId.ARDUINO_MICRO,
});
supportedDevices.put(UsbId.VENDOR_VAN_OOIJEN_TECH,
new int[] {
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
});
supportedDevices.put(UsbId.VENDOR_ATMEL,
new int[] {
UsbId.ATMEL_LUFA_CDC_DEMO_APP,
});
supportedDevices.put(UsbId.VENDOR_LEAFLABS,
new int[] {
UsbId.LEAFLABS_MAPLE,
});
supportedDevices.put(UsbId.VENDOR_ARM,
new int[] {
UsbId.ARM_MBED,
});
supportedDevices.put(UsbId.VENDOR_ST,
new int[] {
UsbId.ST_CDC,
});
return supportedDevices;
}
}

View file

@ -7,11 +7,9 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
//import org.emulator.calculator.usbserial.BuildConfig;
import java.io.IOException;
import java.util.Collections;
@ -22,366 +20,355 @@ import java.util.Map;
public class Ch34xSerialDriver implements UsbSerialDriver {
private static final String TAG = Ch34xSerialDriver.class.getSimpleName();
private static final String TAG = Ch34xSerialDriver.class.getSimpleName();
private final UsbDevice mDevice;
private final UsbSerialPort mPort;
private final UsbDevice mDevice;
private final UsbSerialPort mPort;
private static final int LCR_ENABLE_RX = 0x80;
private static final int LCR_ENABLE_TX = 0x40;
private static final int LCR_MARK_SPACE = 0x20;
private static final int LCR_PAR_EVEN = 0x10;
private static final int LCR_ENABLE_PAR = 0x08;
private static final int LCR_STOP_BITS_2 = 0x04;
private static final int LCR_CS8 = 0x03;
private static final int LCR_CS7 = 0x02;
private static final int LCR_CS6 = 0x01;
private static final int LCR_CS5 = 0x00;
private static final int LCR_ENABLE_RX = 0x80;
private static final int LCR_ENABLE_TX = 0x40;
private static final int LCR_MARK_SPACE = 0x20;
private static final int LCR_PAR_EVEN = 0x10;
private static final int LCR_ENABLE_PAR = 0x08;
private static final int LCR_STOP_BITS_2 = 0x04;
private static final int LCR_CS8 = 0x03;
private static final int LCR_CS7 = 0x02;
private static final int LCR_CS6 = 0x01;
private static final int LCR_CS5 = 0x00;
private static final int GCL_CTS = 0x01;
private static final int GCL_DSR = 0x02;
private static final int GCL_RI = 0x04;
private static final int GCL_CD = 0x08;
private static final int SCL_DTR = 0x20;
private static final int SCL_RTS = 0x40;
private static final int GCL_CTS = 0x01;
private static final int GCL_DSR = 0x02;
private static final int GCL_RI = 0x04;
private static final int GCL_CD = 0x08;
private static final int SCL_DTR = 0x20;
private static final int SCL_RTS = 0x40;
public Ch34xSerialDriver(UsbDevice device) {
mDevice = device;
mPort = new Ch340SerialPort(mDevice, 0);
}
public Ch34xSerialDriver(UsbDevice device) {
mDevice = device;
mPort = new Ch340SerialPort(mDevice, 0);
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
@Override
public List<UsbSerialPort> getPorts() {
return Collections.singletonList(mPort);
}
@Override
public List<UsbSerialPort> getPorts() {
return Collections.singletonList(mPort);
}
public class Ch340SerialPort extends CommonUsbSerialPort {
public class Ch340SerialPort extends CommonUsbSerialPort {
private static final int USB_TIMEOUT_MILLIS = 5000;
private static final int USB_TIMEOUT_MILLIS = 5000;
private final int DEFAULT_BAUD_RATE = 9600;
private final int DEFAULT_BAUD_RATE = 9600;
private boolean dtr = false;
private boolean rts = false;
private boolean dtr = false;
private boolean rts = false;
public Ch340SerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
}
public Ch340SerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
}
@Override
public UsbSerialDriver getDriver() {
return Ch34xSerialDriver.this;
}
@Override
public UsbSerialDriver getDriver() {
return Ch34xSerialDriver.this;
}
@Override
protected void openInt() throws IOException {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbIface = mDevice.getInterface(i);
if (!mConnection.claimInterface(usbIface, true)) {
throw new IOException("Could not claim data interface");
}
}
@Override
protected void openInt(UsbDeviceConnection connection) throws IOException {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbIface = mDevice.getInterface(i);
if (!mConnection.claimInterface(usbIface, true)) {
throw new IOException("Could not claim data interface");
}
}
UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
UsbEndpoint ep = dataIface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
mReadEndpoint = ep;
} else {
mWriteEndpoint = ep;
}
}
}
UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
UsbEndpoint ep = dataIface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
mReadEndpoint = ep;
} else {
mWriteEndpoint = ep;
}
}
}
initialize();
setBaudRate(DEFAULT_BAUD_RATE);
}
initialize();
setBaudRate(DEFAULT_BAUD_RATE);
}
@Override
protected void closeInt() {
try {
for (int i = 0; i < mDevice.getInterfaceCount(); i++)
mConnection.releaseInterface(mDevice.getInterface(i));
} catch(Exception ignored) {}
}
@Override
protected void closeInt() {
try {
for (int i = 0; i < mDevice.getInterfaceCount(); i++)
mConnection.releaseInterface(mDevice.getInterface(i));
} catch(Exception ignored) {}
}
private int controlOut(int request, int value, int index) {
final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT;
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request,
value, index, null, 0, USB_TIMEOUT_MILLIS);
}
private int controlOut(int request, int value, int index) {
final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT;
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request,
value, index, null, 0, USB_TIMEOUT_MILLIS);
}
private int controlIn(int request, int value, int index, byte[] buffer) {
final int REQTYPE_DEVICE_TO_HOST = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_IN;
return mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, request,
value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS);
}
private int controlIn(int request, int value, int index, byte[] buffer) {
final int REQTYPE_DEVICE_TO_HOST = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_IN;
return mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, request,
value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS);
}
private void checkState(String msg, int request, int value, int[] expected) throws IOException {
byte[] buffer = new byte[expected.length];
int ret = controlIn(request, value, 0, buffer);
private void checkState(String msg, int request, int value, int[] expected) throws IOException {
byte[] buffer = new byte[expected.length];
int ret = controlIn(request, value, 0, buffer);
if (ret < 0) {
throw new IOException("Failed send cmd [" + msg + "]");
}
if (ret < 0) {
throw new IOException("Failed send cmd [" + msg + "]");
}
if (ret != expected.length) {
throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]");
}
if (ret != expected.length) {
throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]");
}
for (int i = 0; i < expected.length; i++) {
if (expected[i] == -1) {
continue;
}
for (int i = 0; i < expected.length; i++) {
if (expected[i] == -1) {
continue;
}
int current = buffer[i] & 0xff;
if (expected[i] != current) {
throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]");
}
}
}
int current = buffer[i] & 0xff;
if (expected[i] != current) {
throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]");
}
}
}
private void setControlLines() throws IOException {
if (controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0) < 0) {
throw new IOException("Failed to set control lines");
}
}
private void setControlLines() throws IOException {
if (controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0) < 0) {
throw new IOException("Failed to set control lines");
}
}
private byte getStatus() throws IOException {
byte[] buffer = new byte[2];
int ret = controlIn(0x95, 0x0706, 0, buffer);
if (ret < 0)
throw new IOException("Error getting control lines");
return buffer[0];
}
private byte getStatus() throws IOException {
byte[] buffer = new byte[2];
int ret = controlIn(0x95, 0x0706, 0, buffer);
if (ret < 0)
throw new IOException("Error getting control lines");
return buffer[0];
}
private void initialize() throws IOException {
checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00});
private void initialize() throws IOException {
checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00});
if (controlOut(0xa1, 0, 0) < 0) {
throw new IOException("Init failed: #2");
}
if (controlOut(0xa1, 0, 0) < 0) {
throw new IOException("Init failed: #2");
}
setBaudRate(DEFAULT_BAUD_RATE);
setBaudRate(DEFAULT_BAUD_RATE);
checkState("init #4", 0x95, 0x2518, new int[]{-1 /* 0x56, c3*/, 0x00});
checkState("init #4", 0x95, 0x2518, new int[]{-1 /* 0x56, c3*/, 0x00});
if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) {
throw new IOException("Init failed: #5");
}
if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) {
throw new IOException("Init failed: #5");
}
checkState("init #6", 0x95, 0x0706, new int[]{-1/*0xf?*/, -1/*0xec,0xee*/});
checkState("init #6", 0x95, 0x0706, new int[]{-1/*0xf?*/, -1/*0xec,0xee*/});
if (controlOut(0xa1, 0x501f, 0xd90a) < 0) {
throw new IOException("Init failed: #7");
}
if (controlOut(0xa1, 0x501f, 0xd90a) < 0) {
throw new IOException("Init failed: #7");
}
setBaudRate(DEFAULT_BAUD_RATE);
setBaudRate(DEFAULT_BAUD_RATE);
setControlLines();
setControlLines();
checkState("init #10", 0x95, 0x0706, new int[]{-1/* 0x9f, 0xff*/, -1/*0xec,0xee*/});
}
checkState("init #10", 0x95, 0x0706, new int[]{-1/* 0x9f, 0xff*/, -1/*0xec,0xee*/});
}
private void setBaudRate(int baudRate) throws IOException {
long factor;
long divisor;
private void setBaudRate(int baudRate) throws IOException {
final long CH341_BAUDBASE_FACTOR = 1532620800;
final int CH341_BAUDBASE_DIVMAX = 3;
if (baudRate == 921600) {
divisor = 7;
factor = 0xf300;
} else {
final long BAUDBASE_FACTOR = 1532620800;
final int BAUDBASE_DIVMAX = 3;
long factor = CH341_BAUDBASE_FACTOR / baudRate;
int divisor = CH341_BAUDBASE_DIVMAX;
// if(BuildConfig.DEBUG && (baudRate & (3<<29)) == (1<<29))
// baudRate &= ~(1<<29); // for testing purpose bypass dedicated baud rate handling
factor = BAUDBASE_FACTOR / baudRate;
divisor = BAUDBASE_DIVMAX;
while ((factor > 0xfff0) && divisor > 0) {
factor >>= 3;
divisor--;
}
if (factor > 0xfff0) {
throw new UnsupportedOperationException("Unsupported baud rate: " + baudRate);
}
factor = 0x10000 - factor;
}
while ((factor > 0xfff0) && divisor > 0) {
factor >>= 3;
divisor--;
}
divisor |= 0x0080; // else ch341a waits until buffer full
int val1 = (int) ((factor & 0xff00) | divisor);
int val2 = (int) (factor & 0xff);
Log.d(TAG, String.format("baud rate=%d, 0x1312=0x%04x, 0x0f2c=0x%04x", baudRate, val1, val2));
int ret = controlOut(0x9a, 0x1312, val1);
if (ret < 0) {
throw new IOException("Error setting baud rate: #1)");
}
ret = controlOut(0x9a, 0x0f2c, val2);
if (ret < 0) {
throw new IOException("Error setting baud rate: #2");
}
}
if (factor > 0xfff0) {
throw new UnsupportedOperationException("Unsupported baud rate: " + baudRate);
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException {
if(baudRate <= 0) {
throw new IllegalArgumentException("Invalid baud rate: " + baudRate);
}
setBaudRate(baudRate);
factor = 0x10000 - factor;
divisor |= 0x0080; // else ch341a waits until buffer full
int ret = controlOut(0x9a, 0x1312, (int) ((factor & 0xff00) | divisor));
if (ret < 0) {
throw new IOException("Error setting baud rate: #1)");
}
int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX;
ret = controlOut(0x9a, 0x0f2c, (int) (factor & 0xff));
if (ret < 0) {
throw new IOException("Error setting baud rate: #2");
}
}
switch (dataBits) {
case DATABITS_5:
lcr |= LCR_CS5;
break;
case DATABITS_6:
lcr |= LCR_CS6;
break;
case DATABITS_7:
lcr |= LCR_CS7;
break;
case DATABITS_8:
lcr |= LCR_CS8;
break;
default:
throw new IllegalArgumentException("Invalid data bits: " + dataBits);
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException {
if(baudRate <= 0) {
throw new IllegalArgumentException("Invalid baud rate: " + baudRate);
}
setBaudRate(baudRate);
switch (parity) {
case PARITY_NONE:
break;
case PARITY_ODD:
lcr |= LCR_ENABLE_PAR;
break;
case PARITY_EVEN:
lcr |= LCR_ENABLE_PAR | LCR_PAR_EVEN;
break;
case PARITY_MARK:
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE;
break;
case PARITY_SPACE:
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE | LCR_PAR_EVEN;
break;
default:
throw new IllegalArgumentException("Invalid parity: " + parity);
}
int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX;
switch (stopBits) {
case STOPBITS_1:
break;
case STOPBITS_1_5:
throw new UnsupportedOperationException("Unsupported stop bits: 1.5");
case STOPBITS_2:
lcr |= LCR_STOP_BITS_2;
break;
default:
throw new IllegalArgumentException("Invalid stop bits: " + stopBits);
}
switch (dataBits) {
case DATABITS_5:
lcr |= LCR_CS5;
break;
case DATABITS_6:
lcr |= LCR_CS6;
break;
case DATABITS_7:
lcr |= LCR_CS7;
break;
case DATABITS_8:
lcr |= LCR_CS8;
break;
default:
throw new IllegalArgumentException("Invalid data bits: " + dataBits);
}
int ret = controlOut(0x9a, 0x2518, lcr);
if (ret < 0) {
throw new IOException("Error setting control byte");
}
}
switch (parity) {
case PARITY_NONE:
break;
case PARITY_ODD:
lcr |= LCR_ENABLE_PAR;
break;
case PARITY_EVEN:
lcr |= LCR_ENABLE_PAR | LCR_PAR_EVEN;
break;
case PARITY_MARK:
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE;
break;
case PARITY_SPACE:
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE | LCR_PAR_EVEN;
break;
default:
throw new IllegalArgumentException("Invalid parity: " + parity);
}
@Override
public boolean getCD() throws IOException {
return (getStatus() & GCL_CD) == 0;
}
switch (stopBits) {
case STOPBITS_1:
break;
case STOPBITS_1_5:
throw new UnsupportedOperationException("Unsupported stop bits: 1.5");
case STOPBITS_2:
lcr |= LCR_STOP_BITS_2;
break;
default:
throw new IllegalArgumentException("Invalid stop bits: " + stopBits);
}
@Override
public boolean getCTS() throws IOException {
return (getStatus() & GCL_CTS) == 0;
}
int ret = controlOut(0x9a, 0x2518, lcr);
if (ret < 0) {
throw new IOException("Error setting control byte");
}
}
@Override
public boolean getDSR() throws IOException {
return (getStatus() & GCL_DSR) == 0;
}
@Override
public boolean getCD() throws IOException {
return (getStatus() & GCL_CD) == 0;
}
@Override
public boolean getDTR() throws IOException {
return dtr;
}
@Override
public boolean getCTS() throws IOException {
return (getStatus() & GCL_CTS) == 0;
}
@Override
public void setDTR(boolean value) throws IOException {
dtr = value;
setControlLines();
}
@Override
public boolean getDSR() throws IOException {
return (getStatus() & GCL_DSR) == 0;
}
@Override
public boolean getRI() throws IOException {
return (getStatus() & GCL_RI) == 0;
}
@Override
public boolean getDTR() throws IOException {
return dtr;
}
@Override
public boolean getRTS() throws IOException {
return rts;
}
@Override
public void setDTR(boolean value) throws IOException {
dtr = value;
setControlLines();
}
@Override
public void setRTS(boolean value) throws IOException {
rts = value;
setControlLines();
}
@Override
public boolean getRI() throws IOException {
return (getStatus() & GCL_RI) == 0;
}
@Override
public EnumSet<ControlLine> getControlLines() throws IOException {
int status = getStatus();
EnumSet<ControlLine> set = EnumSet.noneOf(ControlLine.class);
if(rts) set.add(ControlLine.RTS);
if((status & GCL_CTS) == 0) set.add(ControlLine.CTS);
if(dtr) set.add(ControlLine.DTR);
if((status & GCL_DSR) == 0) set.add(ControlLine.DSR);
if((status & GCL_CD) == 0) set.add(ControlLine.CD);
if((status & GCL_RI) == 0) set.add(ControlLine.RI);
return set;
}
@Override
public boolean getRTS() throws IOException {
return rts;
}
@Override
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.allOf(ControlLine.class);
}
@Override
public void setRTS(boolean value) throws IOException {
rts = value;
setControlLines();
}
@Override
public void setBreak(boolean value) throws IOException {
byte[] req = new byte[2];
if(controlIn(0x95, 0x1805, 0, req) < 0) {
throw new IOException("Error getting BREAK condition");
}
if(value) {
req[0] &= ~1;
req[1] &= ~0x40;
} else {
req[0] |= 1;
req[1] |= 0x40;
}
int val = (req[1] & 0xff) << 8 | (req[0] & 0xff);
if(controlOut(0x9a, 0x1805, val) < 0) {
throw new IOException("Error setting BREAK condition");
}
}
}
@Override
public EnumSet<ControlLine> getControlLines() throws IOException {
int status = getStatus();
EnumSet<ControlLine> set = EnumSet.noneOf(ControlLine.class);
if(rts) set.add(ControlLine.RTS);
if((status & GCL_CTS) == 0) set.add(ControlLine.CTS);
if(dtr) set.add(ControlLine.DTR);
if((status & GCL_DSR) == 0) set.add(ControlLine.DSR);
if((status & GCL_CD) == 0) set.add(ControlLine.CD);
if((status & GCL_RI) == 0) set.add(ControlLine.RI);
return set;
}
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{
UsbId.QINHENG_CH340,
UsbId.QINHENG_CH341A,
});
return supportedDevices;
}
@Override
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.allOf(ControlLine.class);
}
@Override
public void setBreak(boolean value) throws IOException {
byte[] req = new byte[2];
if(controlIn(0x95, 0x1805, 0, req) < 0) {
throw new IOException("Error getting BREAK condition");
}
if(value) {
req[0] &= ~1;
req[1] &= ~0x40;
} else {
req[0] |= 1;
req[1] |= 0x40;
}
int val = (req[1] & 0xff) << 8 | (req[0] & 0xff);
if(controlOut(0x9a, 0x1805, val) < 0) {
throw new IOException("Error setting BREAK condition");
}
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{
UsbId.QINHENG_CH340,
UsbId.QINHENG_CH341A,
});
return supportedDevices;
}
}

View file

@ -1,96 +0,0 @@
package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import java.io.IOException;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
public class ChromeCcdSerialDriver implements UsbSerialDriver{
private final String TAG = ChromeCcdSerialDriver.class.getSimpleName();
private final UsbDevice mDevice;
private final List<UsbSerialPort> mPorts;
@Override
public UsbDevice getDevice() {
return mDevice;
}
@Override
public List<UsbSerialPort> getPorts() {
return mPorts;
}
public ChromeCcdSerialDriver(UsbDevice mDevice) {
this.mDevice = mDevice;
mPorts = new ArrayList<UsbSerialPort>();
for (int i = 0; i < 3; i++)
mPorts.add(new ChromeCcdSerialPort(mDevice, i));
}
public class ChromeCcdSerialPort extends CommonUsbSerialPort {
private UsbInterface mDataInterface;
public ChromeCcdSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
}
@Override
protected void openInt() throws IOException {
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
mDataInterface = mDevice.getInterface(mPortNumber);
if (!mConnection.claimInterface(mDataInterface, true)) {
throw new IOException("Could not claim shared control/data interface");
}
Log.d(TAG, "endpoint count=" + mDataInterface.getEndpointCount());
for (int i = 0; i < mDataInterface.getEndpointCount(); ++i) {
UsbEndpoint ep = mDataInterface.getEndpoint(i);
if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
mReadEndpoint = ep;
} else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
mWriteEndpoint = ep;
}
}
}
@Override
protected void closeInt() {
try {
mConnection.releaseInterface(mDataInterface);
} catch(Exception ignored) {}
}
@Override
public UsbSerialDriver getDriver() {
return ChromeCcdSerialDriver.this;
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.noneOf(ControlLine.class);
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_GOOGLE, new int[]{
UsbId.GOOGLE_CR50,
});
return supportedDevices;
}
}

View file

@ -25,32 +25,28 @@ import java.util.EnumSet;
*/
public abstract class CommonUsbSerialPort implements UsbSerialPort {
public static boolean DEBUG = false;
private static final String TAG = CommonUsbSerialPort.class.getSimpleName();
private static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024;
private static final int MAX_READ_SIZE = 16 * 1024; // = old bulkTransfer limit
protected final UsbDevice mDevice;
protected final int mPortNumber;
// non-null when open()
protected UsbDeviceConnection mConnection;
protected UsbDeviceConnection mConnection = null;
protected UsbEndpoint mReadEndpoint;
protected UsbEndpoint mWriteEndpoint;
protected UsbRequest mUsbRequest;
/**
* Internal write buffer.
* Guarded by {@link #mWriteBufferLock}.
* Default length = mReadEndpoint.getMaxPacketSize()
**/
protected byte[] mWriteBuffer;
protected final Object mWriteBufferLock = new Object();
/** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */
protected byte[] mWriteBuffer;
public CommonUsbSerialPort(UsbDevice device, int portNumber) {
mDevice = device;
mPortNumber = portNumber;
mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE];
}
@Override
@ -89,19 +85,11 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
* Sets the size of the internal buffer used to exchange data with the USB
* stack for write operations. Most users should not need to change this.
*
* @param bufferSize the size in bytes, <= 0 resets original size
* @param bufferSize the size in bytes
*/
public final void setWriteBufferSize(int bufferSize) {
synchronized (mWriteBufferLock) {
if (bufferSize <= 0) {
if (mWriteEndpoint != null) {
bufferSize = mWriteEndpoint.getMaxPacketSize();
} else {
mWriteBuffer = null;
return;
}
}
if (mWriteBuffer != null && bufferSize == mWriteBuffer.length) {
if (bufferSize == mWriteBuffer.length) {
return;
}
mWriteBuffer = new byte[bufferSize];
@ -118,7 +106,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
}
mConnection = connection;
try {
openInt();
openInt(connection);
if (mReadEndpoint == null || mWriteEndpoint == null) {
throw new IOException("Could not get read & write endpoints");
}
@ -132,18 +120,17 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
}
}
protected abstract void openInt() throws IOException;
protected abstract void openInt(UsbDeviceConnection connection) throws IOException;
@Override
public void close() throws IOException {
if (mConnection == null) {
throw new IOException("Already closed");
}
UsbRequest usbRequest = mUsbRequest;
mUsbRequest = null;
try {
usbRequest.cancel();
mUsbRequest.cancel();
} catch(Exception ignored) {}
mUsbRequest = null;
try {
closeInt();
} catch(Exception ignored) {}
@ -158,40 +145,25 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
/**
* use simple USB request supported by all devices to test if connection is still valid
*/
protected void testConnection(boolean full) throws IOException {
testConnection(full, "USB get_status request failed");
}
protected void testConnection(boolean full, String msg) throws IOException {
if(mUsbRequest == null) {
throw new IOException("Connection closed");
}
if(!full) {
return;
}
protected void testConnection() throws IOException {
byte[] buf = new byte[2];
int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200);
if(len < 0)
throw new IOException(msg);
throw new IOException("USB get_status request failed");
}
@Override
public int read(final byte[] dest, final int timeout) throws IOException {
if(dest.length == 0) {
throw new IllegalArgumentException("Read buffer too small");
}
return read(dest, dest.length, timeout);
return read(dest, timeout, true);
}
@Override
public int read(final byte[] dest, final int length, final int timeout) throws IOException {return read(dest, length, timeout, true);}
protected int read(final byte[] dest, int length, final int timeout, boolean testConnection) throws IOException {
testConnection(false);
if(length <= 0) {
throw new IllegalArgumentException("Read length too small");
protected int read(final byte[] dest, final int timeout, boolean testConnection) throws IOException {
if(mConnection == null) {
throw new IOException("Connection closed");
}
if(dest.length <= 0) {
throw new IllegalArgumentException("Read buffer to small");
}
length = Math.min(length, dest.length);
final int nread;
if (timeout != 0) {
// bulkTransfer will cause data loss with short timeout + high baud rates + continuous transfer
@ -203,16 +175,16 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
// /system/lib64/libandroid_runtime.so (android_hardware_UsbDeviceConnection_request_wait(_JNIEnv*, _jobject*, long)+84)
// data loss / crashes were observed with timeout up to 200 msec
long endTime = testConnection ? MonotonicClock.millis() + timeout : 0;
int readMax = Math.min(length, MAX_READ_SIZE);
int readMax = Math.min(dest.length, MAX_READ_SIZE);
nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout);
// Android error propagation is improvable:
// nread == -1 can be: timeout, connection lost, buffer to small, ???
if(nread == -1 && testConnection)
testConnection(MonotonicClock.millis() < endTime);
if(nread == -1 && testConnection && MonotonicClock.millis() < endTime)
testConnection();
} else {
final ByteBuffer buf = ByteBuffer.wrap(dest, 0, length);
if (!mUsbRequest.queue(buf, length)) {
final ByteBuffer buf = ByteBuffer.wrap(dest);
if (!mUsbRequest.queue(buf, dest.length)) {
throw new IOException("Queueing USB request failed");
}
final UsbRequest response = mConnection.requestWait();
@ -223,23 +195,21 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
// Android error propagation is improvable:
// response != null & nread == 0 can be: connection lost, buffer to small, ???
if(nread == 0) {
testConnection(true);
testConnection();
}
}
return Math.max(nread, 0);
}
@Override
public void write(byte[] src, int timeout) throws IOException {write(src, src.length, timeout);}
@Override
public void write(final byte[] src, int length, final int timeout) throws IOException {
public void write(final byte[] src, final int timeout) throws IOException {
int offset = 0;
long startTime = MonotonicClock.millis();
length = Math.min(length, src.length);
final long endTime = (timeout == 0) ? 0 : (MonotonicClock.millis() + timeout);
testConnection(false);
while (offset < length) {
if(mConnection == null) {
throw new IOException("Connection closed");
}
while (offset < src.length) {
int requestTimeout;
final int requestLength;
final int actualLength;
@ -247,10 +217,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
synchronized (mWriteBufferLock) {
final byte[] writeBuffer;
if (mWriteBuffer == null) {
mWriteBuffer = new byte[mWriteEndpoint.getMaxPacketSize()];
}
requestLength = Math.min(length - offset, mWriteBuffer.length);
requestLength = Math.min(src.length - offset, mWriteBuffer.length);
if (offset == 0) {
writeBuffer = src;
} else {
@ -261,7 +228,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
if (timeout == 0 || offset == 0) {
requestTimeout = timeout;
} else {
requestTimeout = (int)(startTime + timeout - MonotonicClock.millis());
requestTimeout = (int)(endTime - MonotonicClock.millis());
if(requestTimeout == 0)
requestTimeout = -1;
}
@ -271,18 +238,14 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
actualLength = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, requestLength, requestTimeout);
}
}
long elapsed = MonotonicClock.millis() - startTime;
if (DEBUG) {
Log.d(TAG, "Wrote " + actualLength + "/" + requestLength + " offset " + offset + "/" + length + " time " + elapsed + "/" + requestTimeout);
}
Log.d(TAG, "Wrote " + actualLength + "/" + requestLength + " offset " + offset + "/" + src.length + " timeout " + requestTimeout);
if (actualLength <= 0) {
String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength;
if (timeout != 0) {
testConnection(elapsed < timeout, msg);
throw new SerialTimeoutException(msg, offset);
if (timeout != 0 && MonotonicClock.millis() >= endTime) {
SerialTimeoutException ex = new SerialTimeoutException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + ", rc=" + actualLength);
ex.bytesTransferred = offset;
throw ex;
} else {
throw new IOException(msg);
throw new IOException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length);
}
}
offset += actualLength;
@ -291,7 +254,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
@Override
public boolean isOpen() {
return mUsbRequest != null;
return mConnection != null;
}
@Override
@ -322,7 +285,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
public void setRTS(boolean value) throws IOException { throw new UnsupportedOperationException(); }
@Override
public EnumSet<ControlLine> getControlLines() throws IOException { throw new UnsupportedOperationException(); }
public abstract EnumSet<ControlLine> getControlLines() throws IOException;
@Override
public abstract EnumSet<ControlLine> getSupportedControlLines() throws IOException;

View file

@ -8,6 +8,7 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
@ -118,14 +119,14 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
byte[] buffer = new byte[1];
int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, SILABSER_GET_MDMSTS_REQUEST_CODE, 0,
mPortNumber, buffer, buffer.length, USB_WRITE_TIMEOUT_MILLIS);
if (result != buffer.length) {
if (result != 1) {
throw new IOException("Control transfer failed: " + SILABSER_GET_MDMSTS_REQUEST_CODE + " / " + 0 + " -> " + result);
}
return buffer[0];
}
@Override
protected void openInt() throws IOException {
protected void openInt(UsbDeviceConnection connection) throws IOException {
mIsRestrictedPort = mDevice.getInterfaceCount() == 2 && mPortNumber == 1;
if(mPortNumber >= mDevice.getInterfaceCount()) {
throw new IOException("Unknown port number");
@ -320,7 +321,6 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
}
}
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_SILABS,

View file

@ -9,6 +9,7 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.util.Log;
import org.emulator.calculator.usbserial.util.MonotonicClock;
@ -98,8 +99,8 @@ public class FtdiSerialDriver implements UsbSerialDriver {
@Override
protected void openInt() throws IOException {
if (!mConnection.claimInterface(mDevice.getInterface(mPortNumber), true)) {
protected void openInt(UsbDeviceConnection connection) throws IOException {
if (!connection.claimInterface(mDevice.getInterface(mPortNumber), true)) {
throw new IOException("Could not claim interface " + mPortNumber);
}
if (mDevice.getInterface(mPortNumber).getEndpointCount() < 2) {
@ -122,13 +123,12 @@ public class FtdiSerialDriver implements UsbSerialDriver {
}
// mDevice.getVersion() would require API 23
byte[] rawDescriptors = mConnection.getRawDescriptors();
byte[] rawDescriptors = connection.getRawDescriptors();
if(rawDescriptors == null || rawDescriptors.length < 14) {
throw new IOException("Could not get device descriptors");
}
int deviceType = rawDescriptors[13];
baudRateWithPort = deviceType == 7 || deviceType == 8 || deviceType == 9 // ...H devices
|| mDevice.getInterfaceCount() > 1; // FT2232C
baudRateWithPort = deviceType == 7 || deviceType == 8 || deviceType == 9; // ...H devices
}
@Override
@ -139,37 +139,24 @@ public class FtdiSerialDriver implements UsbSerialDriver {
}
@Override
public int read(final byte[] dest, final int timeout) throws IOException
{
public int read(final byte[] dest, final int timeout) throws IOException {
if(dest.length <= READ_HEADER_LENGTH) {
throw new IllegalArgumentException("Read buffer too small");
throw new IllegalArgumentException("Read buffer to small");
// could allocate larger buffer, including space for 2 header bytes, but this would
// result in buffers not being 64 byte aligned any more, causing data loss at continuous
// data transfer at high baud rates when buffers are fully filled.
}
return read(dest, dest.length, timeout);
}
@Override
public int read(final byte[] dest, int length, final int timeout) throws IOException {
if(length <= READ_HEADER_LENGTH) {
throw new IllegalArgumentException("Read length too small");
// could allocate larger buffer, including space for 2 header bytes, but this would
// result in buffers not being 64 byte aligned any more, causing data loss at continuous
// data transfer at high baud rates when buffers are fully filled.
}
length = Math.min(length, dest.length);
int nread;
if (timeout != 0) {
long endTime = MonotonicClock.millis() + timeout;
do {
nread = super.read(dest, length, Math.max(1, (int)(endTime - MonotonicClock.millis())), false);
nread = super.read(dest, Math.max(1, (int)(endTime - MonotonicClock.millis())), false);
} while (nread == READ_HEADER_LENGTH && MonotonicClock.millis() < endTime);
if(nread <= 0)
testConnection(MonotonicClock.millis() < endTime);
if(nread <= 0 && MonotonicClock.millis() < endTime)
testConnection();
} else {
do {
nread = super.read(dest, length, timeout);
nread = super.read(dest, timeout, false);
} while (nread == READ_HEADER_LENGTH);
}
return readFilter(dest, nread);
@ -303,7 +290,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
byte[] data = new byte[2];
int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_MODEM_STATUS_REQUEST,
0, mPortNumber+1, data, data.length, USB_WRITE_TIMEOUT_MILLIS);
if (result != data.length) {
if (result != 2) {
throw new IOException("Get modem status failed: result=" + result);
}
return data[0];
@ -419,7 +406,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
byte[] data = new byte[1];
int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_LATENCY_TIMER_REQUEST,
0, mPortNumber+1, data, data.length, USB_WRITE_TIMEOUT_MILLIS);
if (result != data.length) {
if (result != 1) {
throw new IOException("Get latency timer failed: result=" + result);
}
return data[0];
@ -427,7 +414,6 @@ public class FtdiSerialDriver implements UsbSerialDriver {
}
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_FTDI,

View file

@ -1,106 +0,0 @@
package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import java.io.IOException;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class GsmModemSerialDriver implements UsbSerialDriver{
private final String TAG = GsmModemSerialDriver.class.getSimpleName();
private final UsbDevice mDevice;
private final UsbSerialPort mPort;
@Override
public UsbDevice getDevice() {
return mDevice;
}
@Override
public List<UsbSerialPort> getPorts() {
return Collections.singletonList(mPort);
}
public GsmModemSerialDriver(UsbDevice mDevice) {
this.mDevice = mDevice;
mPort = new GsmModemSerialPort(mDevice, 0);
}
public class GsmModemSerialPort extends CommonUsbSerialPort {
private UsbInterface mDataInterface;
public GsmModemSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
}
@Override
protected void openInt() throws IOException {
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
mDataInterface = mDevice.getInterface(0);
if (!mConnection.claimInterface(mDataInterface, true)) {
throw new IOException("Could not claim shared control/data interface");
}
Log.d(TAG, "endpoint count=" + mDataInterface.getEndpointCount());
for (int i = 0; i < mDataInterface.getEndpointCount(); ++i) {
UsbEndpoint ep = mDataInterface.getEndpoint(i);
if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
mReadEndpoint = ep;
} else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
mWriteEndpoint = ep;
}
}
initGsmModem();
}
@Override
protected void closeInt() {
try {
mConnection.releaseInterface(mDataInterface);
} catch(Exception ignored) {}
}
private int initGsmModem() throws IOException {
int len = mConnection.controlTransfer(
0x21, 0x22, 0x01, 0, null, 0, 5000);
if(len < 0) {
throw new IOException("init failed");
}
return len;
}
@Override
public UsbSerialDriver getDriver() {
return GsmModemSerialDriver.this;
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.noneOf(ControlLine.class);
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_UNISOC, new int[]{
UsbId.FIBOCOM_L610,
UsbId.FIBOCOM_L612,
});
return supportedDevices;
}
}

View file

@ -6,7 +6,6 @@
package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbDevice;
import android.util.Pair;
import java.lang.reflect.InvocationTargetException;
@ -15,14 +14,14 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
* Maps (vendor id, product id) pairs to the corresponding serial driver,
* or invoke 'probe' method to check actual USB devices for matching interfaces.
* Maps (vendor id, product id) pairs to the corresponding serial driver.
*
* @author mike wakerly (opensource@hoho.com)
*/
public class ProbeTable {
private final Map<Pair<Integer, Integer>, Class<? extends UsbSerialDriver>> mVidPidProbeTable =
private final Map<Pair<Integer, Integer>, Class<? extends UsbSerialDriver>> mProbeTable =
new LinkedHashMap<>();
private final Map<Method, Class<? extends UsbSerialDriver>> mMethodProbeTable = new LinkedHashMap<>();
/**
* Adds or updates a (vendor, product) pair in the table.
@ -34,7 +33,7 @@ public class ProbeTable {
*/
public ProbeTable addProduct(int vendorId, int productId,
Class<? extends UsbSerialDriver> driverClass) {
mVidPidProbeTable.put(Pair.create(vendorId, productId), driverClass);
mProbeTable.put(Pair.create(vendorId, productId), driverClass);
return this;
}
@ -42,11 +41,12 @@ public class ProbeTable {
* Internal method to add all supported products from
* {@code getSupportedProducts} static method.
*
* @param driverClass to be added
* @param driverClass
* @return
*/
@SuppressWarnings("unchecked")
void addDriver(Class<? extends UsbSerialDriver> driverClass) {
Method method;
ProbeTable addDriver(Class<? extends UsbSerialDriver> driverClass) {
final Method method;
try {
method = driverClass.getMethod("getSupportedDevices");
@ -68,35 +68,20 @@ public class ProbeTable {
}
}
try {
method = driverClass.getMethod("probe", UsbDevice.class);
mMethodProbeTable.put(method, driverClass);
} catch (SecurityException | NoSuchMethodException ignored) {
}
return this;
}
/**
* Returns the driver for the given USB device, or {@code null} if no match.
* Returns the driver for the given (vendor, product) pair, or {@code null}
* if no match.
*
* @param usbDevice the USB device to be probed
* @param vendorId the USB vendor id
* @param productId the USB product id
* @return the driver class matching this pair, or {@code null}
*/
public Class<? extends UsbSerialDriver> findDriver(final UsbDevice usbDevice) {
final Pair<Integer, Integer> pair = Pair.create(usbDevice.getVendorId(), usbDevice.getProductId());
Class<? extends UsbSerialDriver> driverClass = mVidPidProbeTable.get(pair);
if (driverClass != null)
return driverClass;
for (Map.Entry<Method, Class<? extends UsbSerialDriver>> entry : mMethodProbeTable.entrySet()) {
try {
Method method = entry.getKey();
Object o = method.invoke(null, usbDevice);
if((boolean)o)
return entry.getValue();
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
return null;
public Class<? extends UsbSerialDriver> findDriver(int vendorId, int productId) {
final Pair<Integer, Integer> pair = Pair.create(vendorId, productId);
return mProbeTable.get(pair);
}
}

View file

@ -11,6 +11,7 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
@ -34,7 +35,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
28800, 38400, 57600, 115200, 128000, 134400, 161280, 201600, 230400, 268800,
403200, 460800, 614400, 806400, 921600, 1228800, 2457600, 3000000, 6000000
};
protected enum DeviceType { DEVICE_TYPE_01, DEVICE_TYPE_T, DEVICE_TYPE_HX, DEVICE_TYPE_HXN }
protected enum DeviceType { DEVICE_TYPE_01, DEVICE_TYPE_T, DEVICE_TYPE_HX, DEVICE_TYPE_HXN}
private final UsbDevice mDevice;
private final UsbSerialPort mPort;
@ -122,7 +123,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
private volatile Thread mReadStatusThread = null;
private final Object mReadStatusThreadLock = new Object();
private boolean mStopReadStatusThread = false;
private Exception mReadStatusException = null;
private IOException mReadStatusException = null;
public ProlificSerialPort(UsbDevice device, int portNumber) {
@ -138,7 +139,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
byte[] buffer = new byte[length];
int result = mConnection.controlTransfer(requestType, request, value, index, buffer, length, USB_READ_TIMEOUT_MILLIS);
if (result != length) {
throw new IOException(String.format("ControlTransfer %s 0x%x failed: %d",mDeviceType.name(), value, result));
throw new IOException(String.format("ControlTransfer 0x%x failed: %d",value, result));
}
return buffer;
}
@ -147,7 +148,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
int length = (data == null) ? 0 : data.length;
int result = mConnection.controlTransfer(requestType, request, value, index, data, length, USB_WRITE_TIMEOUT_MILLIS);
if (result != length) {
throw new IOException( String.format("ControlTransfer %s 0x%x failed: %d", mDeviceType.name(), value, result));
throw new IOException( String.format("ControlTransfer 0x%x failed: %d", value, result));
}
}
@ -201,12 +202,12 @@ public class ProlificSerialDriver implements UsbSerialDriver {
private void readStatusThreadFunction() {
try {
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
while (!mStopReadStatusThread) {
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
long endTime = MonotonicClock.millis() + 500;
int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500);
if(readBytesCount == -1)
testConnection(MonotonicClock.millis() < endTime);
if(readBytesCount == -1 && MonotonicClock.millis() < endTime)
testConnection();
if (readBytesCount > 0) {
if (readBytesCount != STATUS_BUFFER_SIZE) {
throw new IOException("Invalid status notification, expected " + STATUS_BUFFER_SIZE + " bytes, got " + readBytesCount);
@ -217,9 +218,8 @@ public class ProlificSerialDriver implements UsbSerialDriver {
}
}
}
} catch (Exception e) {
if (isOpen())
mReadStatusException = e;
} catch (IOException e) {
mReadStatusException = e;
}
//Log.d(TAG, "end control line status thread " + mStopReadStatusThread + " " + (mReadStatusException == null ? "-" : mReadStatusException.getMessage()));
}
@ -250,8 +250,8 @@ public class ProlificSerialDriver implements UsbSerialDriver {
}
}
/* throw and clear an exception which occurred in the status read thread */
Exception readStatusException = mReadStatusException;
/* throw and clear an exception which occured in the status read thread */
IOException readStatusException = mReadStatusException;
if (mReadStatusException != null) {
mReadStatusException = null;
throw new IOException(readStatusException);
@ -265,10 +265,10 @@ public class ProlificSerialDriver implements UsbSerialDriver {
}
@Override
public void openInt() throws IOException {
public void openInt(UsbDeviceConnection connection) throws IOException {
UsbInterface usbInterface = mDevice.getInterface(0);
if (!mConnection.claimInterface(usbInterface, true)) {
if (!connection.claimInterface(usbInterface, true)) {
throw new IOException("Error claiming Prolific interface 0");
}
@ -290,7 +290,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
}
}
byte[] rawDescriptors = mConnection.getRawDescriptors();
byte[] rawDescriptors = connection.getRawDescriptors();
if(rawDescriptors == null || rawDescriptors.length < 14) {
throw new IOException("Could not get device descriptors");
}
@ -299,19 +299,15 @@ public class ProlificSerialDriver implements UsbSerialDriver {
byte maxPacketSize0 = rawDescriptors[7];
if (mDevice.getDeviceClass() == 0x02 || maxPacketSize0 != 64) {
mDeviceType = DeviceType.DEVICE_TYPE_01;
} else if(usbVersion == 0x200) {
if(deviceVersion == 0x300 && testHxStatus()) {
mDeviceType = DeviceType.DEVICE_TYPE_T; // TA
} else if(deviceVersion == 0x500 && testHxStatus()) {
mDeviceType = DeviceType.DEVICE_TYPE_T; // TB
} else {
mDeviceType = DeviceType.DEVICE_TYPE_HXN;
}
} else if(deviceVersion == 0x300 && usbVersion == 0x200) {
mDeviceType = DeviceType.DEVICE_TYPE_T; // TA
} else if(deviceVersion == 0x500) {
mDeviceType = DeviceType.DEVICE_TYPE_T; // TB
} else if(usbVersion == 0x200 && !testHxStatus()) {
mDeviceType = DeviceType.DEVICE_TYPE_HXN;
} else {
mDeviceType = DeviceType.DEVICE_TYPE_HX;
}
Log.d(TAG, String.format("usbVersion=%x, deviceVersion=%x, deviceClass=%d, packetSize=%d => deviceType=%s",
usbVersion, deviceVersion, mDevice.getDeviceClass(), maxPacketSize0, mDeviceType.name()));
resetDevice();
doBlackMagic();
setControlLines(mControlLinesValue);
@ -567,7 +563,6 @@ public class ProlificSerialDriver implements UsbSerialDriver {
}
}
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_PROLIFIC,
@ -576,6 +571,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
UsbId.PROLIFIC_PL2303GC,
UsbId.PROLIFIC_PL2303GB,
UsbId.PROLIFIC_PL2303GT,
UsbId.PROLIFIC_PL2303GT3,
UsbId.PROLIFIC_PL2303GL,
UsbId.PROLIFIC_PL2303GE,
UsbId.PROLIFIC_PL2303GS,

View file

@ -9,8 +9,7 @@ import java.io.InterruptedIOException;
* {@see InterruptedIOException#bytesTransferred} may contain bytes transferred
*/
public class SerialTimeoutException extends InterruptedIOException {
public SerialTimeoutException(String s, int bytesTransferred) {
public SerialTimeoutException(String s) {
super(s);
this.bytesTransferred = bytesTransferred;
}
}

View file

@ -23,6 +23,27 @@ public final class UsbId {
public static final int FTDI_FT232H = 0x6014;
public static final int FTDI_FT231X = 0x6015; // same ID for FT230X, FT231X, FT234XD
public static final int VENDOR_ATMEL = 0x03EB;
public static final int ATMEL_LUFA_CDC_DEMO_APP = 0x2044;
public static final int VENDOR_ARDUINO = 0x2341;
public static final int ARDUINO_UNO = 0x0001;
public static final int ARDUINO_MEGA_2560 = 0x0010;
public static final int ARDUINO_SERIAL_ADAPTER = 0x003b;
public static final int ARDUINO_MEGA_ADK = 0x003f;
public static final int ARDUINO_MEGA_2560_R3 = 0x0042;
public static final int ARDUINO_UNO_R3 = 0x0043;
public static final int ARDUINO_MEGA_ADK_R3 = 0x0044;
public static final int ARDUINO_SERIAL_ADAPTER_R3 = 0x0044;
public static final int ARDUINO_LEONARDO = 0x8036;
public static final int ARDUINO_MICRO = 0x8037;
public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0;
public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483;
public static final int VENDOR_LEAFLABS = 0x1eaf;
public static final int LEAFLABS_MAPLE = 0x0004;
public static final int VENDOR_SILABS = 0x10c4;
public static final int SILABS_CP2102 = 0xea60; // same ID for CP2101, CP2103, CP2104, CP2109
public static final int SILABS_CP2105 = 0xea70;
@ -32,22 +53,22 @@ public final class UsbId {
public static final int PROLIFIC_PL2303 = 0x2303; // device type 01, T, HX
public static final int PROLIFIC_PL2303GC = 0x23a3; // device type HXN
public static final int PROLIFIC_PL2303GB = 0x23b3; // "
public static final int PROLIFIC_PL2303GT = 0x23c3; // "
public static final int PROLIFIC_PL2303GL = 0x23d3; // "
public static final int PROLIFIC_PL2303GT = 0x23cd; // "
public static final int PROLIFIC_PL2303GT3 = 0x23c3; // "
public static final int PROLIFIC_PL2303GL = 0x23e3; // "
public static final int PROLIFIC_PL2303GE = 0x23e3; // "
public static final int PROLIFIC_PL2303GS = 0x23f3; // "
public static final int VENDOR_GOOGLE = 0x18d1;
public static final int GOOGLE_CR50 = 0x5014;
public static final int VENDOR_QINHENG = 0x1a86;
public static final int QINHENG_CH340 = 0x7523;
public static final int QINHENG_CH341A = 0x5523;
public static final int VENDOR_UNISOC = 0x1782;
public static final int FIBOCOM_L610 = 0x4D10;
public static final int FIBOCOM_L612 = 0x4D12;
// at www.linux-usb.org/usb.ids listed for NXP/LPC1768, but all processors supported by ARM mbed DAPLink firmware report these ids
public static final int VENDOR_ARM = 0x0d28;
public static final int ARM_MBED = 0x0204;
public static final int VENDOR_ST = 0x0483;
public static final int ST_CDC = 0x5740;
private UsbId() {
throw new IllegalAccessError("Non-instantiable class");

View file

@ -10,17 +10,12 @@ import android.hardware.usb.UsbDevice;
import java.util.List;
/**
*
* @author mike wakerly (opensource@hoho.com)
*/
public interface UsbSerialDriver {
/*
* Additional interface properties. Invoked thru reflection.
*
UsbSerialDriver(UsbDevice device); // constructor with device
static Map<Integer, int[]> getSupportedDevices();
static boolean probe(UsbDevice device); // optional
*/
/**
* Returns the raw {@link UsbDevice} backing this port.
*

View file

@ -58,7 +58,7 @@ public interface UsbSerialPort extends Closeable {
int STOPBITS_2 = 2;
/** Values for get[Supported]ControlLines() */
enum ControlLine { RTS, CTS, DTR, DSR, CD, RI }
enum ControlLine { RTS, CTS, DTR, DSR, CD, RI }
/**
* Returns the driver used by this port.
@ -122,17 +122,6 @@ public interface UsbSerialPort extends Closeable {
*/
int read(final byte[] dest, final int timeout) throws IOException;
/**
* Reads bytes with specified length into the destination buffer.
*
* @param dest the destination byte buffer
* @param length the maximum length of the data to read
* @param timeout the timeout for reading in milliseconds, 0 is infinite
* @return the actual number of bytes read
* @throws IOException if an error occurred during reading
*/
int read(final byte[] dest, int length, final int timeout) throws IOException;
/**
* Writes as many bytes as possible from the source buffer.
*
@ -144,18 +133,6 @@ public interface UsbSerialPort extends Closeable {
*/
void write(final byte[] src, final int timeout) throws IOException;
/**
* Writes bytes with specified length from the source buffer.
*
* @param src the source byte buffer
* @param length the length of the data to write
* @param timeout the timeout for writing in milliseconds, 0 is infinite
* @throws SerialTimeoutException if timeout reached before sending all data.
* ex.bytesTransferred may contain bytes transferred
* @throws IOException if an error occurred during writing
*/
void write(final byte[] src, int length, final int timeout) throws IOException;
/**
* Sets various serial port parameters.
*

View file

@ -37,8 +37,6 @@ public class UsbSerialProber {
probeTable.addDriver(FtdiSerialDriver.class);
probeTable.addDriver(ProlificSerialDriver.class);
probeTable.addDriver(Ch34xSerialDriver.class);
probeTable.addDriver(GsmModemSerialDriver.class);
probeTable.addDriver(ChromeCcdSerialDriver.class);
return probeTable;
}
@ -71,7 +69,11 @@ public class UsbSerialProber {
* {@code null} if none available.
*/
public UsbSerialDriver probeDevice(final UsbDevice usbDevice) {
final Class<? extends UsbSerialDriver> driverClass = mProbeTable.findDriver(usbDevice);
final int vendorId = usbDevice.getVendorId();
final int productId = usbDevice.getProductId();
final Class<? extends UsbSerialDriver> driverClass =
mProbeTable.findDriver(vendorId, productId);
if (driverClass != null) {
final UsbSerialDriver driver;
try {

View file

@ -1,158 +0,0 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.emulator.calculator.usbserial.util;
import java.security.InvalidParameterException;
/**
* Clone of Android's /core/java/com/android/internal/util/HexDump class, for use in debugging.
* Changes: space separated hex strings
*/
public class HexDump {
private final static char[] HEX_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
private HexDump() {
}
public static String dumpHexString(byte[] array) {
return dumpHexString(array, 0, array.length);
}
public static String dumpHexString(byte[] array, int offset, int length) {
StringBuilder result = new StringBuilder();
byte[] line = new byte[8];
int lineIndex = 0;
for (int i = offset; i < offset + length; i++) {
if (lineIndex == line.length) {
for (int j = 0; j < line.length; j++) {
if (line[j] > ' ' && line[j] < '~') {
result.append(new String(line, j, 1));
} else {
result.append(".");
}
}
result.append("\n");
lineIndex = 0;
}
byte b = array[i];
result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
result.append(HEX_DIGITS[b & 0x0F]);
result.append(" ");
line[lineIndex++] = b;
}
for (int i = 0; i < (line.length - lineIndex); i++) {
result.append(" ");
}
for (int i = 0; i < lineIndex; i++) {
if (line[i] > ' ' && line[i] < '~') {
result.append(new String(line, i, 1));
} else {
result.append(".");
}
}
return result.toString();
}
public static String toHexString(byte b) {
return toHexString(toByteArray(b));
}
public static String toHexString(byte[] array) {
return toHexString(array, 0, array.length);
}
public static String toHexString(byte[] array, int offset, int length) {
char[] buf = new char[length > 0 ? length * 3 - 1 : 0];
int bufIndex = 0;
for (int i = offset; i < offset + length; i++) {
if (i > offset)
buf[bufIndex++] = ' ';
byte b = array[i];
buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
}
return new String(buf);
}
public static String toHexString(int i) {
return toHexString(toByteArray(i));
}
public static String toHexString(short i) {
return toHexString(toByteArray(i));
}
public static byte[] toByteArray(byte b) {
byte[] array = new byte[1];
array[0] = b;
return array;
}
public static byte[] toByteArray(int i) {
byte[] array = new byte[4];
array[3] = (byte) (i & 0xFF);
array[2] = (byte) ((i >> 8) & 0xFF);
array[1] = (byte) ((i >> 16) & 0xFF);
array[0] = (byte) ((i >> 24) & 0xFF);
return array;
}
public static byte[] toByteArray(short i) {
byte[] array = new byte[2];
array[1] = (byte) (i & 0xFF);
array[0] = (byte) ((i >> 8) & 0xFF);
return array;
}
private static int toByte(char c) {
if (c >= '0' && c <= '9')
return (c - '0');
if (c >= 'A' && c <= 'F')
return (c - 'A' + 10);
if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
throw new InvalidParameterException("Invalid hex char '" + c + "'");
}
/** accepts any separator, e.g. space or newline */
public static byte[] hexStringToByteArray(String hexString) {
int length = hexString.length();
byte[] buffer = new byte[(length + 1) / 3];
for (int i = 0; i < length; i += 3) {
buffer[i / 3] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i + 1)));
}
return buffer;
}
}

View file

@ -9,7 +9,6 @@ package org.emulator.calculator.usbserial.util;
import android.os.Process;
import android.util.Log;
import org.emulator.calculator.usbserial.driver.SerialTimeoutException;
import org.emulator.calculator.usbserial.driver.UsbSerialPort;
import java.io.IOException;
@ -22,15 +21,8 @@ import java.nio.ByteBuffer;
*/
public class SerialInputOutputManager implements Runnable {
public enum State {
STOPPED,
RUNNING,
STOPPING
}
public static boolean DEBUG = false;
private static final String TAG = SerialInputOutputManager.class.getSimpleName();
public static boolean DEBUG = false;
private static final int BUFSIZ = 4096;
/**
@ -45,6 +37,12 @@ public class SerialInputOutputManager implements Runnable {
private ByteBuffer mReadBuffer; // default size = getReadEndpoint().getMaxPacketSize()
private ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ);
public enum State {
STOPPED,
RUNNING,
STOPPING
}
private int mThreadPriority = Process.THREAD_PRIORITY_URGENT_AUDIO;
private State mState = State.STOPPED; // Synchronized by 'this'
private Listener mListener; // Synchronized by 'this'
@ -204,11 +202,7 @@ public class SerialInputOutputManager implements Runnable {
step();
}
} catch (Exception e) {
if(mSerialPort.isOpen()) {
Log.w(TAG, "Run ending due to exception: " + e.getMessage(), e);
} else {
Log.i(TAG, "Socket closed");
}
Log.w(TAG, "Run ending due to exception: " + e.getMessage(), e);
final Listener listener = getListener();
if (listener != null) {
listener.onRunError(e);
@ -229,9 +223,7 @@ public class SerialInputOutputManager implements Runnable {
}
int len = mSerialPort.read(buffer, mReadTimeout);
if (len > 0) {
if (DEBUG) {
Log.d(TAG, "Read data len=" + len);
}
if (DEBUG) Log.d(TAG, "Read data len=" + len);
final Listener listener = getListener();
if (listener != null) {
final byte[] data = new byte[len];
@ -255,23 +247,7 @@ public class SerialInputOutputManager implements Runnable {
if (DEBUG) {
Log.d(TAG, "Writing data len=" + len);
}
try {
mSerialPort.write(buffer, mWriteTimeout);
} catch (SerialTimeoutException ex) {
synchronized (mWriteBufferLock) {
byte[] buffer2 = null;
int len2 = mWriteBuffer.position();
if (len2 > 0) {
buffer2 = new byte[len2];
mWriteBuffer.rewind();
mWriteBuffer.get(buffer2, 0, len2);
mWriteBuffer.clear();
}
mWriteBuffer.put(buffer, ex.bytesTransferred, buffer.length - ex.bytesTransferred);
if (buffer2 != null)
mWriteBuffer.put(buffer2);
}
}
mSerialPort.write(buffer, mWriteTimeout);
}
}

View file

@ -1,33 +0,0 @@
package org.emulator.calculator.usbserial.util;
import android.hardware.usb.UsbDeviceConnection;
import java.util.ArrayList;
public class UsbUtils {
private UsbUtils() {
}
public static ArrayList<byte[]> getDescriptors(UsbDeviceConnection connection) {
ArrayList<byte[]> descriptors = new ArrayList<>();
byte[] rawDescriptors = connection.getRawDescriptors();
if (rawDescriptors != null) {
int pos = 0;
while (pos < rawDescriptors.length) {
int len = rawDescriptors[pos] & 0xFF;
if (len == 0)
break;
if (pos + len > rawDescriptors.length)
len = rawDescriptors.length - pos;
byte[] descriptor = new byte[len];
System.arraycopy(rawDescriptors, pos, descriptor, 0, len);
descriptors.add(descriptor);
pos += len;
}
}
return descriptors;
}
}

View file

@ -140,13 +140,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Most Recently Used state files
private final int MRU_ID_START = 10000;
private final int MAX_MRU = 5;
private LinkedHashMap<String, String> mruLinkedHashMap = new LinkedHashMap<String, String>(5, 1.0f, true) {
private final LinkedHashMap<String, String> mruLinkedHashMap = new LinkedHashMap<String, String>(5, 1.0f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_MRU;
}
};
private HashMap<Integer, String> mruByMenuId = new HashMap<>();
private final HashMap<Integer, String> mruByMenuId = new HashMap<>();
private final PrinterSimulator printerSimulator = new PrinterSimulator();
private final PrinterSimulatorFragment fragmentPrinterSimulator = new PrinterSimulatorFragment();
@ -246,7 +246,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
}
if(documentToOpenUrl != null && documentToOpenUrl.length() > 0)
if(documentToOpenUrl != null && !documentToOpenUrl.isEmpty())
try {
// FileOpen auto-open.
onFileOpen(documentToOpenUrl, intent, null);
@ -276,7 +276,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
for (int i = mrus.length - 1; i >= 0; i--) {
String mostRecentlyUsedFile = mrus[i];
String displayName = getFilenameFromURL(mostRecentlyUsedFile);
if(displayName == null || displayName.equals("") || displayName.equals(mostRecentlyUsedFile)) {
if(displayName == null || displayName.isEmpty() || displayName.equals(mostRecentlyUsedFile)) {
// We should remove this file because it seems impossible to get the display name of this Most Recently Used state file.
// It might be deleted or the permissions does not allow to reach it anymore.
mruLinkedHashMap.remove(mostRecentlyUsedFile);
@ -301,7 +301,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if (NativeLib.isDocumentAvailable() && settings.getBoolean("settings_autosave", true)) {
String currentFilename = NativeLib.getCurrentFilename();
if (currentFilename != null && currentFilename.length() > 0)
if (currentFilename != null && !currentFilename.isEmpty())
onFileSave();
}
@ -1140,7 +1140,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
saveWhenLaunchingActivity = false;
startActivityForResult(intent, INTENT_LOAD_FLASH_ROM);
};
if(currentFlashPort2Url != null && currentFlashPort2Url.length() > 0)
if(currentFlashPort2Url != null && !currentFlashPort2Url.isEmpty())
new AlertDialog.Builder(this)
.setTitle(getString(R.string.alert_losing_flash_rom_title))
.setMessage(getString(R.string.alert_losing_flash_rom_message))
@ -1165,8 +1165,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Reset to default Flash ROM from the KML file
settings.putString("settings_flash_port2", null);
String kmlFilename = NativeLib.getCurrentKml();
if(kmlFilename.length() > 0) {
String kmlFolder = kmlFolderURL == null || kmlFolderURL.length() == 0 ? null : kmlFolderURL;
if(!kmlFilename.isEmpty()) {
String kmlFolder = kmlFolderURL == null || kmlFolderURL.isEmpty() ? null : kmlFolderURL;
// Reset the flashROM
NativeLib.onLoadFlashROM("");
// Load the KML file again. TODO If it goes wrong, we are lost.
@ -1237,7 +1237,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
updateFromPreferences("settings_flash_port2", true);
displayFilename(url);
String settingsFlashPort2Url = settings.getString("settings_flash_port2", null);
if(settingsFlashPort2Url != null && settingsFlashPort2Url.length() > 0)
if(settingsFlashPort2Url != null && !settingsFlashPort2Url.isEmpty())
showAlert(String.format(Locale.US, getString(R.string.message_state_and_flash_saved), getFilenameFromURL(url), getFilenameFromURL(settingsFlashPort2Url)));
else
showAlert(String.format(Locale.US, getString(R.string.message_state_saved), getFilenameFromURL(url)));
@ -1344,8 +1344,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Not possible to save the current FlashROM :-( [NativeLib.onSaveFlashROM(url);]
// So, we are going to create a new one from the ROM loaded by the current KML script!
String kmlFilename = NativeLib.getCurrentKml();
if(kmlFilename != null && kmlFilename.length() > 0) {
if(kmlFolderURL != null && kmlFolderURL.length() > 0)
if(kmlFilename != null && !kmlFilename.isEmpty()) {
if(kmlFolderURL != null && !kmlFolderURL.isEmpty())
copyROMFromFolder(kmlFilename, uri);
else
copyROMFromAsset(kmlFilename, uri);
@ -1453,7 +1453,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
}
}
if(romFilename != null && romFilename.length() > 0) {
if(romFilename != null && !romFilename.isEmpty()) {
ParcelFileDescriptor pfdROM = openFileInFolderFromContentResolverPFD(romFilename, kmlFolderURL, GENERIC_READ);
if(pfdROM != null) {
try {
@ -1479,7 +1479,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
try {
kmlInputStream = assetManager.open("calculators/" + kmlFilename);
String romFilename = extractROMFilename(kmlInputStream);
if(romFilename != null && romFilename.length() > 0) {
if(romFilename != null && !romFilename.isEmpty()) {
InputStream romInputStream = assetManager.open("calculators/" + romFilename);
copyROMtoFlashROM(romInputStream, uri);
}
@ -1583,7 +1583,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if(getPackageName().contains("org.emulator.forty.eight") && settings.getBoolean("settings_port2en", false)) {
// Check if the access to the port2 shared file is still possible.
String port2Url = settings.getString("settings_port2load", "");
if(port2Url != null && port2Url.length() > 0) {
if(port2Url != null && !port2Url.isEmpty()) {
Uri port2Uri = Uri.parse(port2Url);
DocumentFile port2DocumentFile = DocumentFile.fromSingleUri(this, port2Uri);
if (port2DocumentFile == null || !port2DocumentFile.exists()) {
@ -1706,7 +1706,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
updateFromPreferences("settings_flash_port2", true);
String settingsFlashPort2Url = settings.getString("settings_flash_port2", null);
if(settingsFlashPort2Url != null && settingsFlashPort2Url.length() > 0)
if(settingsFlashPort2Url != null && !settingsFlashPort2Url.isEmpty())
showAlert(String.format(Locale.US, getString(R.string.message_state_and_flash_saved), getFilenameFromURL(currentFilenameUrl), getFilenameFromURL(settingsFlashPort2Url)));
else
showAlert(String.format(Locale.US, getString(R.string.message_state_saved), getFilenameFromURL(currentFilenameUrl)));
@ -1716,7 +1716,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void displayFilename(String stateFileURL) {
String displayName = getFilenameFromURL(stateFileURL == null ? "" : stateFileURL);
String port2FileURL = settings.getString("settings_flash_port2", null);
if(port2FileURL != null && port2FileURL.length() > 0)
if(port2FileURL != null && !port2FileURL.isEmpty())
displayName += " " + getFilenameFromURL(port2FileURL);
View headerView = displayKMLTitle();
if(headerView != null) {
@ -2082,14 +2082,14 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
private int serialIndex = 1;
private HashMap<Integer, Serial> serialsById = new HashMap<>();
private final HashMap<Integer, Serial> serialsById = new HashMap<>();
@SuppressWarnings("UnusedDeclaration")
int openSerialPort(String serialPort) {
// Search if this same serial port is not already opened
Integer serialPortId = serialIndex;
int serialPortId = serialIndex;
Serial serial = new Serial(this, serialPortId);
if(serial.connect(serialPort)) {
serialsById.put(serialPortId, serial);
@ -2104,10 +2104,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
String connectionStatus = serial.getConnectionStatus();
try {
int resId = Utils.resId(MainActivity.this, "string", connectionStatus);
Toast.makeText(MainActivity.this, resId, Toast.LENGTH_LONG).show();
Toast.makeText(MainActivity.this, resId, Toast.LENGTH_SHORT).show();
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
Toast.makeText(MainActivity.this, "Unknown error, connectionStatus: " + connectionStatus, Toast.LENGTH_LONG).show();
Log.e(TAG, "Unknown error, connectionStatus: " + connectionStatus + ", " + ex.getMessage());
Toast.makeText(MainActivity.this, "Unknown error, connectionStatus: " + connectionStatus, Toast.LENGTH_SHORT).show();
}
});
return 0;
@ -2200,7 +2200,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
private void setPort1Settings(boolean port1Plugged, boolean port1Writable) {
if(this.getClass().getPackage().getName().equals("org.emulator.forty.eight")) {
Package pack = this.getClass().getPackage();
if(pack != null && pack.getName().equals("org.emulator.forty.eight")) {
settings.putBoolean("settings_port1en", port1Plugged);
settings.putBoolean("settings_port1wr", port1Writable);
updateFromPreferences("settings_port1", true);
@ -2224,7 +2225,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
switch (key) {
case "settings_realspeed":
case "settings_grayscale":
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null);
case "settings_serial_slowdown":
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null);
break;
case "settings_rotation":
@ -2347,9 +2349,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
NativeLib.setConfiguration("settings_serial_ports_ir", isDynamicValue, 0, 0,
DevicesFragment.SerialConnectParameters.fromSettingsString(settings.getString("settings_serial_ports_ir", "")).toWin32String());
break;
case "settings_serial_slowdown":
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null);
break;
}
}
}

View file

@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.4.2'
classpath 'com.android.tools.build:gradle:8.6.1'
// NOTE: Do not place your application dependencies here; they belong

View file

@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.defaults.buildfeatures.buildconfig=true
#android.defaults.buildfeatures.buildconfig=true
android.enableJetifier=true
android.nonFinalResIds=true
android.nonTransitiveRClass=true

View file

@ -1,6 +1,6 @@
#Sat Jan 28 21:16:38 CET 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME