hmatuschek / qdmr

A GUI application for configuring and programming cheap DMR radios under Linux and MacOS X.
https://dm3mat.darc.de/qdmr/
GNU General Public License v3.0
223 stars 46 forks source link

Add support for cs800d #233

Open johnhste opened 2 years ago

johnhste commented 2 years ago

another new radio issue i am willing to learn but haven't touched c++ at all. i attached a code plug and some wireshark captures from a windows vm to the radio.

cps radio read and write.zip cs800d.rdb.zip

johnhste commented 2 years ago

as an update to above the cs750 also appears to use the same coms method. I am attaching a capture of that as wel cs750 capture.CSV l

johnhste commented 2 years ago

cs800d write.CSV cs800d read.CSV I captured with windows process monitor a read and write event.

hmatuschek commented 2 years ago

The process monitor just captures calls to the windows API, I actually need some wireshark captures of the USB traffic to inspect the protocol used by the CPS to talk to the radio. If it is a serial over USB (USB-ACM) type of protocol, the chances are good that I can implement it without having the radio in my hands. However, it will still take some time to do it.

An introduction can be found under

https://wiki.wireshark.org/CaptureSetup/USB#windows

I usually run windows as a virtual machine and then capture the USB traffic at the Linux host. However, you should be able to capture it directly on the windows machine.

johnhste commented 2 years ago

The initial message has Wireshark captures in a zip file. I can redo it separately. if that would help

hmatuschek commented 2 years ago

Oh, sorry I haven't looked into it. However, it only contains capures of a USB mass-storage device. Does the radio appear as a usb flash drive? If not, try to select the specific device to capture in wireshark.

johnhste commented 2 years ago

here is a lsusb with the radio connected lsusb  ✔ Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 003: ID 27c6:609c Shenzhen Goodix Technology Co.,Ltd. Goodix USB2.0 MISC Bus 003 Device 002: ID 0bda:5634 Realtek Semiconductor Corp. Laptop Camera Bus 003 Device 006: ID 0483:5720 STMicroelectronics Mass Storage Device Bus 003 Device 004: ID 8087:0025 Intel Corp. Wireless-AC 9260 Bluetooth Adapter Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 002: ID 13fe:6500 Kingston Technology Company Inc. USB DISK 3.2 Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub with the radio disconnected lsusb  ✔ Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 003: ID 27c6:609c Shenzhen Goodix Technology Co.,Ltd. Goodix USB2.0 MISC Bus 003 Device 002: ID 0bda:5634 Realtek Semiconductor Corp. Laptop Camera Bus 003 Device 004: ID 8087:0025 Intel Corp. Wireless-AC 9260 Bluetooth Adapter Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 002: ID 13fe:6500 Kingston Technology Company Inc. USB DISK 3.2 Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

new captures for the radio cs800d write.zip cs800d read .zip

johnhste commented 2 years ago

USBMS is the protocol

johnhste commented 2 years ago

for the write the filter device is usb.device_address == 11

for the read the filter device is usb.device_address == 15

johnhste commented 2 years ago

Screenshot 2022-08-03 071642 device manager with radio connected

hmatuschek commented 2 years ago

Oh, weird. They use the flash-drive protocol to write to and read from the device. This is kind of interesting. To this end, I may only need to read/write files on that drive. Lets see.

johnhste commented 2 years ago

is there anything I could try on my end. I happen to have the radio and my laptop with me at work today.

hmatuschek commented 2 years ago

Thanks a lot, it somewhat makes sense to misuse the USB mass storage protocol. It is actually less weird then some other protocols I've seen. I.e., Radioddity uses something like a HID protocol, usually for USB keyboards and mouses. I'll have a look at it.

johnhste commented 2 years ago

thanks. if there is anything I can do to help just let me know.

hmatuschek commented 2 years ago

I've had a look at it and I can see how the codeplug is written into the device, reading however still is a mystery. I do not see any significant data being read from the device using the USBMS proto.

johnhste commented 2 years ago

wireshark in this image isn't the wall of usbms response data the read data from the radio

hmatuschek commented 2 years ago

Oh, yes sorry. Looked at the wrong USBMS device.

johnhste commented 2 years ago

No problem. I did that when trying to get the message written yesterday.

johnhste commented 2 years ago

I have been messing with pyusb and the radio haven't figured out how the message is arcitected.

johnhste commented 2 years ago

From my poking around it looks like it is sending the radio something then the radio is responding with information about the requested data

hmatuschek commented 2 years ago

I've created a branch cs800d, where I document my reverse engineering.

johnhste commented 2 years ago

Wow looks like you made a lot of progress

johnhste commented 2 years ago

would having remote access to a radio connected to my laptop help. we could set that up if you were interested.

johnhste commented 2 years ago

i was messing with sg_raw in terminal if i send ff 28 00 00 00 00 00 00 00 00 00 00 00 00 47 50 i get 1024 bytes of data all 0

hmatuschek commented 2 years ago

You have to send an actual command to the device as a request payload. I do not know sg_raw, but there is likely some means to do that. E.g., send

SCSI raw: ff 2a 00 00 00 00 00 00 00 00 00 00 00 00 47 50 Payload: a1 00 03 00 00 00 00 a4

and then send SCSI raw: ff 28 00 00 00 00 00 00 00 00 00 00 00 00 47 50

The device should then return some information about itself.

Btw, I've implemented a python script filtering and decoding the packets in the pcap files.

johnhste commented 2 years ago

sudo sg_raw -r 1k /dev/sg1 ff 2a 00 00 00 00 00 00 00 00 00 00 00 00 47 50  1 ✘ SCSI Status: Check Condition

Sense Information: Fixed format, current; Sense key: Illegal Request Additional sense: Invalid command operation code

Error 9 occurred, no data received    ~ 

hmatuschek commented 2 years ago

The first command does not trigger the device to send any data. You have to send the mentioned payload using the -i option. The second command then queries the result from the device. There some data is send back.

johnhste commented 2 years ago

sudo sg_raw -r 1k /dev/sg1 ff 2a 00 00 00 00 47 50 -i a1 00 03 00 00 00 00 a4  ✔ NVMe Result=0x2 No data received

johnhste commented 2 years ago

Usage: sg_raw [OPTION]* DEVICE [CDB0 CDB1 ...]

Options: --binary|-b Dump data in binary form, even when writing to stdout --cmdfile=CF|-c CF CF is file containing command in hex bytes --cmdset=CS|-C CS CS is 0 (def) heuristic chooses command set; 1: force SCSI; 2: force NVMe --enumerate|-e Decodes cdb name then exits; requires DEVICE but ignores it --help|-h Show this message and exit --infile=IFILE|-i IFILE Read binary data to send (i.e. data-out) from IFILE (default: stdin) --nosense|-n Don't display sense information --nvm|-N command is for NVM command set (e.g. Read); default, if NVMe fd, Admin command set --outfile=OFILE|-o OFILE Write binary data from device (i.e. data-in) to OFILE (def: hexdump to stdout) --raw|-w interpret CF (command file) as binary (def: interpret as ASCII hex) --readonly|-R Open DEVICE read-only (default: read-write) --request=RLEN|-r RLEN Request up to RLEN bytes of data (data-in) --scan=FO,LO|-Q FO,LO scan command set from FO (first opcode) to LO (last opcode) inclusive. Uses given command bytes, varying the opcode --send=SLEN|-s SLEN Send SLEN bytes of data (data-out) --skip=KLEN|-k KLEN Skip the first KLEN bytes when reading data to send (default: 0) --timeout=SECS|-t SECS Timeout in seconds (default: 20) --verbose|-v Increase verbosity --version|-V Show version information and exit

Between 6 and 260 command bytes (two hex digits each) can be specified and will be sent to DEVICE. Lengths RLEN, SLEN and KLEN are decimal by default. Bidirectional commands accepted.

hmatuschek commented 2 years ago

Create a binary file containing the payload

a1 00 03 00 00 00 00 a4

The file should not contain the hex string but its binary form. Lets call that file request.bin

Then

sg_raw -i request.bin /dev/sg1 ff 2a 00 00 00 00 00 00 00 00 00 00 00 00 47 50
sg_raw -r 1k /dev/sg1 ff 28 00 00 00 00 00 00 00 00 00 00 00 00 47 50

The second call should receive some data.

johnhste commented 2 years ago

a1 00 03 00 00 00 00 a4 10100001 sudo sg_raw -i request.bin /dev/sg1 ff 2a 00 00 00 00 00 00 00 00 00 00 00 00 47 50  99 ✘  21s 

transport error: Host_status=0x03 [DID_TIME_OUT]

SCSI Status: Good

hmatuschek commented 2 years ago

Ok, then it appears to be much harder to talk to the radio. However, after extracting the codeplug from the captures, it appears like the saved archive files are just one-to-one binary dumps of what is written to the device. So I can reverse engineer the codeplug without needing the device in my hands.

johnhste commented 2 years ago

a1 00 03 00 00 00 00 a4 10100001 0 11 0 0 0 0 10100100 i might have had the binary wrong

hmatuschek commented 2 years ago

Oh, yes. You have to generate a binary file from that hex string. Try

echo "a1 00 03 00 00 00 00 a4" | xxd -r -p - request.bin
johnhste commented 2 years ago

request.bin.zip this is what i get sudo sg_raw -i request.bin /dev/sg1 ff 2a 00 00 00 00 00 00 00 00 00 00 00 00 47 50  ✔  37s 

transport error: Host_status=0x03 [DID_TIME_OUT]

SCSI Status: Good

johnhste commented 2 years ago

cs test comm.zip here is the capture of that cmd

hmatuschek commented 2 years ago

I've looked at the capture and the command payload is missing. Maybe sg_raw cannot be used to send those commands.

johnhste commented 2 years ago

got it this morning have to tell it the length of the data packet. and i am telling it that the command is scsi not nvme by the -C1 command sg_raw -C1 -s 8 -i request.bin /dev/sg1 ff 2a 00 00 00 00 00 00 00 00 00 00 00 00 47 50 SCSI Status: Good

filter for usb usb.device_address == 5

cs800d sg_raw 1.zip

johnhste commented 2 years ago

on the request message if i use 256 like is in the original capture it does this sg_raw -r 256 /dev/sg1 ff 28 00 00 00 00 00 00 00 00 00 00 00 00 47 50 SCSI Status: Good

No data received

but if i add a few more bytes i get data.

sg_raw -r 260 /dev/sg1 ff 28 00 00 00 00 00 00 00 00 00 00 00 00 47 50 SCSI Status: Good

Received 260 bytes of data: 00 a1 80 03 01 01 01 43 53 38 30 30 44 00 00 00 00 ......CS800D.... 10 00 00 00 00 00 00 44 34 2e 33 2e 30 32 00 20 22 ......D4.3.02. " 20 08 03 07 13 00 00 44 30 30 30 30 30 31 00 00 00 ......D000001... 30 00 00 00 00 00 00 32 33 30 34 44 37 35 32 34 33 ......2304D75243 40 30 30 30 32 00 00 56 32 2e 32 00 00 00 00 4d 53 0002..V2.2....MS 50 2e 33 30 2e 33 34 00 00 00 00 00 00 00 00 05 01 .30.34.......... 60 00 00 00 00 17 00 00 00 00 00 10 08 07 00 4d 31 ..............M1 70 30 30 30 31 30 00 80 40 20 10 08 04 02 01 52 32 00010..@ .....R2 80 2e 30 35 44 00 00 01 00 00 00 00 00 00 00 00 00 .05D............ 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 100 00 00 00 00 ....

johnhste commented 2 years ago

same command works on the cs750

g_raw -C1 -s 8 -i request.bin /dev/sg1 ff 2a 00 00 00 00 00 00 00 00 00 00 00 00 47 50 SCSI Status: Good

   ~  sg_raw -r 260 /dev/sg1 ff 28 00 00 00 00 00 00 00 00 00 00 00 00 47 50 SCSI Status: Good

Received 260 bytes of data: 00 a1 80 03 01 01 01 43 53 37 35 30 00 00 00 00 00 ......CS750..... 10 00 00 00 00 00 00 44 34 2e 33 2e 30 31 00 20 22 ......D4.3.01. " 20 08 05 07 36 00 00 44 30 30 30 30 30 31 00 00 00 ...6..D000001... 30 00 00 00 00 00 00 32 32 30 31 44 35 35 32 34 34 ......2201D55244 40 30 30 30 33 00 00 48 57 55 2d 35 00 00 00 53 34 0003..HWU-5...S4 50 2e 30 30 2e 30 39 00 00 00 00 00 00 00 00 00 00 .00.09.......... 60 00 00 00 00 16 00 00 00 00 00 e0 90 06 00 4d 31 ..............M1 70 30 30 31 30 30 00 80 40 20 10 08 04 02 01 44 32 00100..@ .....D2 80 2e 30 34 00 00 00 00 00 00 00 00 00 00 00 00 00 .04............. 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 100 00 00 00 00 ..

johnhste commented 2 years ago

got it to work in a bash script. figured that would be easier than tying to get the python plugin that has almost no docs to work. script_1.zip

hmatuschek commented 2 years ago

Ok, this is a good sign. Now we only need to get it working using libusb.

johnhste commented 2 years ago

i found some code on git-hub. it at least seams to be a start on making this work not sure how to modify it to do what we want but shows it is possible. sb.zip i changed the USB vid and pid to the cs750 ones since that is a smaller radio i am using it as my test device.

johnhste commented 2 years ago

i forgot to include the usb cap. id is 6 this time cs750 cap 1.zip e

johnhste commented 2 years ago

cs750 cap 9.zip testcode.zip i made some progress but a little stumped on how to get it to push the correct amount of bytes. the usb id is 10 on the capture file.

hmatuschek commented 2 years ago

Looks almost good. Just the length.

johnhste commented 2 years ago

i am not sure how that is set i have been editing the function writeSettings_smartbend as a test. i set the length to 8. i thought that would do it but it seems to get its length from somewhere else in the code.

johnhste commented 2 years ago

to me the command response data looks the same. i still can't get it to respond with the data block i am expecting.
do you see anything obvious. testdata.zip

johnhste commented 2 years ago

I forgot to say USB id 9

johnhste commented 1 year ago

read first block.zip i got it to work. usb id 17