mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-03-17 05:17:04 -04:00
USB device port support WIP
https://github.com/meshtastic/Meshtastic-Android/issues/38
This commit is contained in:
@@ -85,6 +85,7 @@ class RadioInterfaceService : Service(), Logging {
|
||||
val rest = address.substring(1)
|
||||
val isValid = when (c) {
|
||||
'x' -> BluetoothInterface.addressValid(context, rest)
|
||||
'u' -> SerialInterface.addressValid(context, rest)
|
||||
else -> true
|
||||
}
|
||||
if (!isValid)
|
||||
|
||||
@@ -21,38 +21,62 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
|
||||
val drivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
|
||||
val devices = drivers.map { it.device }
|
||||
devices.forEach { d ->
|
||||
debug("Found serial port $d")
|
||||
debug("Found serial port ${d.deviceName}")
|
||||
}
|
||||
return drivers
|
||||
}
|
||||
|
||||
fun addressValid(context: Context, rest: String): Boolean {
|
||||
findSerial(context, rest)?.let { d ->
|
||||
val manager = context.getSystemService(Context.USB_SERVICE) as UsbManager
|
||||
return manager.hasPermission(d.device)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun findSerial(context: Context, rest: String): UsbSerialDriver? {
|
||||
val drivers = findDrivers(context)
|
||||
|
||||
return if (drivers.isEmpty())
|
||||
null
|
||||
else // Open a connection to the first available driver.
|
||||
drivers[0] // FIXME, instead we should find by name
|
||||
}
|
||||
}
|
||||
|
||||
private var uart: UsbSerialPort?
|
||||
private var uart: UsbSerialPort? = null
|
||||
private lateinit var reader: Thread
|
||||
|
||||
init {
|
||||
val manager = service.getSystemService(Context.USB_SERVICE) as UsbManager
|
||||
val drivers = findDrivers(this)
|
||||
|
||||
// Open a connection to the first available driver.
|
||||
val device = drivers[0].device
|
||||
val device = findSerial(service, address)
|
||||
|
||||
info("Opening $device")
|
||||
val connection = manager.openDevice(device)
|
||||
if (connection == null) {
|
||||
// FIXME add UsbManager.requestPermission(device, ..) handling to activity
|
||||
TODO("Need permissions for port")
|
||||
if (device != null) {
|
||||
info("Opening $device")
|
||||
val connection = manager.openDevice(device.device)
|
||||
if (connection == null) {
|
||||
// FIXME add UsbManager.requestPermission(device, ..) handling to activity
|
||||
TODO("Need permissions for port")
|
||||
} else {
|
||||
val port = device.ports[0] // Most devices have just one port (port 0)
|
||||
|
||||
port.open(connection)
|
||||
port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
|
||||
uart = port
|
||||
|
||||
debug("Starting serial reader thread")
|
||||
// FIXME, start reading thread
|
||||
reader =
|
||||
thread(
|
||||
start = true,
|
||||
isDaemon = true,
|
||||
name = "serial reader",
|
||||
block = ::readerLoop
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val port = drivers[0].ports[0] // Most devices have just one port (port 0)
|
||||
|
||||
port.open(connection)
|
||||
port.setParameters(921600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE)
|
||||
uart = port
|
||||
|
||||
debug("Starting serial reader thread")
|
||||
// FIXME, start reading thread
|
||||
reader =
|
||||
thread(start = true, isDaemon = true, name = "serial reader", block = ::readerLoop)
|
||||
errormsg("Can't find device")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,40 +109,49 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
|
||||
var msb = 0
|
||||
var lsb = 0
|
||||
|
||||
val timeout = (60 * 1000)
|
||||
|
||||
while (uart != null) { // we run until our port gets closed
|
||||
uart?.apply {
|
||||
read(scratch, 0)
|
||||
val c = scratch[0]
|
||||
val numRead = read(scratch, timeout)
|
||||
if (numRead == 0)
|
||||
errormsg("Read returned zero bytes")
|
||||
else {
|
||||
val c = scratch[0]
|
||||
|
||||
// Assume we will be advancing our pointer
|
||||
var nextPtr = ptr + 1
|
||||
// Assume we will be advancing our pointer
|
||||
var nextPtr = ptr + 1
|
||||
|
||||
when (ptr) {
|
||||
0 -> // looking for START1
|
||||
if (c != START1) {
|
||||
debugOut(c)
|
||||
nextPtr = 0 // Restart from scratch
|
||||
when (ptr) {
|
||||
0 -> // looking for START1
|
||||
if (c != START1) {
|
||||
debugOut(c)
|
||||
nextPtr = 0 // Restart from scratch
|
||||
}
|
||||
1 -> // Looking for START2
|
||||
if (c != START2)
|
||||
nextPtr = 0 // Restart from scratch
|
||||
2 -> // Looking for MSB of our 16 bit length
|
||||
msb = c.toInt() and 0xff
|
||||
3 -> // Looking for LSB of our 16 bit length
|
||||
lsb = c.toInt() and 0xff
|
||||
else -> { // We've read our header, do one big read for the packet itself
|
||||
val packetLen = (msb shl 8) or lsb
|
||||
|
||||
// If packet len is too long, the bytes must have been corrupted, start looking for START1 again
|
||||
if (packetLen <= MAX_TO_FROM_RADIO_SIZE) {
|
||||
val buf = ByteArray(packetLen)
|
||||
val numRead = read(buf, timeout)
|
||||
if (numRead < packetLen)
|
||||
errormsg("Packet read was too short")
|
||||
else
|
||||
service.handleFromRadio(buf)
|
||||
}
|
||||
nextPtr = 0 // Start parsing the next packet
|
||||
}
|
||||
1 -> // Looking for START2
|
||||
if (c != START2)
|
||||
nextPtr = 0 // Restart from scratch
|
||||
2 -> // Looking for MSB of our 16 bit length
|
||||
msb = c.toInt() and 0xff
|
||||
3 -> // Looking for LSB of our 16 bit length
|
||||
lsb = c.toInt() and 0xff
|
||||
else -> { // We've read our header, do one big read for the packet itself
|
||||
val packetLen = (msb shl 8) or lsb
|
||||
|
||||
// If packet len is too long, the bytes must have been corrupted, start looking for START1 again
|
||||
if (packetLen <= MAX_TO_FROM_RADIO_SIZE) {
|
||||
val buf = ByteArray(packetLen)
|
||||
read(buf, 0)
|
||||
service.handleFromRadio(buf)
|
||||
}
|
||||
nextPtr = 0 // Start parsing the next packet
|
||||
}
|
||||
ptr = nextPtr
|
||||
}
|
||||
ptr = nextPtr
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.geeksville.mesh.service.SerialInterface
|
||||
import com.geeksville.util.anonymize
|
||||
import com.geeksville.util.exceptionReporter
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver
|
||||
import kotlinx.android.synthetic.main.settings_fragment.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -103,7 +104,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
debug("BTScanModel created")
|
||||
}
|
||||
|
||||
data class DeviceListEntry(val name: String, val address: String, val bonded: Boolean) {
|
||||
open class DeviceListEntry(val name: String, val address: String, val bonded: Boolean) {
|
||||
val bluetoothAddress
|
||||
get() =
|
||||
if (address[0] == 'x')
|
||||
@@ -115,11 +116,17 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
override fun toString(): String {
|
||||
return "DeviceListEntry(name=${name.anonymize}, addr=${address.anonymize})"
|
||||
}
|
||||
|
||||
val isBluetooth: Boolean get() = name[0] == 'x'
|
||||
val isSerial: Boolean get() = name[0] == 's'
|
||||
|
||||
val isBluetooth: Boolean get() = address[0] == 'x'
|
||||
val isSerial: Boolean get() = address[0] == 's'
|
||||
}
|
||||
|
||||
class USBDeviceListEntry(usbManager: UsbManager, val usb: UsbSerialDriver) : DeviceListEntry(
|
||||
usb.device.deviceName,
|
||||
"s${usb.device.deviceName}",
|
||||
usbManager.hasPermission(usb.device)
|
||||
)
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
debug("BTScanModel cleared")
|
||||
@@ -248,17 +255,12 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
addDevice(DeviceListEntry(context.getString(R.string.none), "n", true))
|
||||
|
||||
SerialInterface.findDrivers(context).forEach { d ->
|
||||
val hasPerms = usbManager.hasPermission(d.device)
|
||||
addDevice(
|
||||
DeviceListEntry(
|
||||
d.device.deviceName,
|
||||
"s${d.device.deviceName}",
|
||||
hasPerms
|
||||
)
|
||||
USBDeviceListEntry(usbManager, d)
|
||||
)
|
||||
}
|
||||
|
||||
// filter and only accept devices that have a sw update service
|
||||
// filter and only accept devices that have our service
|
||||
val filter =
|
||||
ScanFilter.Builder()
|
||||
.setServiceUuid(ParcelUuid(BluetoothInterface.BTM_SERVICE_UUID))
|
||||
@@ -307,6 +309,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
return true
|
||||
} else {
|
||||
// Handle requestng USB or bluetooth permissions for the device
|
||||
debug("Requesting permissions for the device")
|
||||
|
||||
if (it.isBluetooth) {
|
||||
// Request bonding for bluetooth
|
||||
@@ -332,6 +335,8 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
}
|
||||
|
||||
if (it.isSerial) {
|
||||
it as USBDeviceListEntry
|
||||
|
||||
val ACTION_USB_PERMISSION = "com.geeksville.mesh.USB_PERMISSION"
|
||||
|
||||
val usbReceiver = object : BroadcastReceiver() {
|
||||
@@ -339,21 +344,19 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (ACTION_USB_PERMISSION == intent.action) {
|
||||
|
||||
val device: UsbDevice? =
|
||||
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
|
||||
val device: UsbDevice =
|
||||
intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)!!
|
||||
|
||||
if (intent.getBooleanExtra(
|
||||
UsbManager.EXTRA_PERMISSION_GRANTED,
|
||||
false
|
||||
)
|
||||
) {
|
||||
device?.apply {
|
||||
info("User approved USB access")
|
||||
changeScanSelection(activity, it.address)
|
||||
info("User approved USB access")
|
||||
changeScanSelection(activity, it.address)
|
||||
|
||||
// Force the GUI to redraw
|
||||
devices.value = devices.value
|
||||
}
|
||||
// Force the GUI to redraw
|
||||
devices.value = devices.value
|
||||
} else {
|
||||
errormsg("USB permission denied for device $device")
|
||||
}
|
||||
@@ -367,7 +370,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
|
||||
PendingIntent.getBroadcast(activity, 0, Intent(ACTION_USB_PERMISSION), 0)
|
||||
val filter = IntentFilter(ACTION_USB_PERMISSION)
|
||||
activity.registerReceiver(usbReceiver, filter)
|
||||
usbManager.requestPermission(device, permissionIntent)
|
||||
usbManager.requestPermission(it.usb.device, permissionIntent)
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user