67 Commits
0.2 ... 0.3

Author SHA1 Message Date
Harald Welte
a601635885 host: Allow matching of device to USB path, not just address
USB addresses change every time the device re-enumerates, while the path
reflects the physical topology of USB connections and stays persistent
unless the usb cabling is changed.  Let's allow the user to specify the
path instead of the address to uniquely identify a given slot.
2017-05-11 01:31:45 +02:00
Harald Welte
37ad41e092 host: Ignore some more libusb error return codes
It seems that sometimes we get even an LIBUSB_ERROR_IO and failing on
errors breaks the application, while ignoring it works.  Not sure why
that really is, but for now it increases reliability.
2017-05-11 01:13:58 +02:00
Harald Welte
66de830f55 host: Switch SIM to remote and issue modem reset on startup
as soon as simtrace2-remotesim is started, we issue the command to
instruct the use of the remote SIM card and ask the modem to reset
itself (to start using the new remote sim).
2017-05-11 01:13:04 +02:00
Harald Welte
23c00b6ad3 cardemu: Fix interpretation of MSGC_MODEM messages
msg->l2h should always point at the first byte of the header after the
generic simtrace header.  For some reason this doesn't work if used
globally, but it is required for the MSGC_MODEM messages to be
interpreted correctly.
2017-05-11 01:11:43 +02:00
Harald Welte
253991789a dfu: make sure to not bloat the loader with main board init stuff. 2017-05-11 00:47:29 +02:00
Harald Welte
0709d2d842 perst/sim_switch: Log events using TRACE_INFO 2017-05-11 00:06:00 +02:00
Harald Welte
44622dfd8d consistently use 0-based counting of modems/slots 2017-05-11 00:04:50 +02:00
Harald Welte
b91f6ad848 firmware: fix various compiler warnings 2017-05-10 23:20:50 +02:00
Harald Welte
9f38dddfcc host: fix various compiler warnings 2017-05-10 23:20:50 +02:00
Harald Welte
d03960525e host: Split transport from slot
In current implementations, we have one interface (with it's own
separate set of USB end-points) per slot.  However, the USB protocol
already includes a slot-number in the header to be able to remove that
restriction in future versions.

Also, if the USB protocol is used remotely over a network, then we could
multiplex differnt slots from different USB interfaces into one stream
on the network side.

In order to prepare the data structures in the host program, let's
introduce that logical split there, too.  Might seem a bit like
overkill, but I don't want to rewrite all code later...
2017-05-10 23:20:46 +02:00
Harald Welte
08b77ba9ee host: split the transport layer from card emulation
We want to be able to use some code irrespective of the application
(emulation, tracing, ...), so let's split the transport struct out of
the cardem_instance.
2017-05-09 15:11:53 +02:00
Harald Welte
296c401275 host: cosmetic cleanup, function renaming 2017-05-09 15:02:52 +02:00
Harald Welte
5c583d3535 Generalize SIM switching code and allow local/remote switching via USB 2017-05-09 13:24:28 +02:00
Harald Welte
114e74d322 wwan_perst: Warn if somebody failed to initialize us 2017-05-09 13:24:28 +02:00
Harald Welte
2e9254ac3f cardem: Implement WWAN Modem reset via USB
Using the SIMTRACE_MSGT_DT_MODEM_RESET message
2017-05-09 13:24:28 +02:00
Harald Welte
cb093ce878 wwan_perst: Support varying timeout and permanent active/inactive 2017-05-09 13:24:28 +02:00
Harald Welte
25a9a80ff5 Convert to new generalized SIMTRACE2 USB protocol
The current protocol was card-emulation specific.  The new protocol is
generic/flexible enough to accommodate both tracing and card emulation,
as well as modem control and other future extensions.
2017-05-09 13:24:23 +02:00
Harald Welte
ed1efc5035 qmod: debug_cmd: Avoid 'Unknown command' for valid commands 2017-05-07 23:10:02 +02:00
Harald Welte
f8c83a4d64 Make hardfault handler a bit more verbose 2017-05-07 23:10:02 +02:00
Harald Welte
28174982b6 qmod: Initialize ST12 specific I/O pins only on ST12 2017-05-07 23:10:02 +02:00
Harald Welte
8e7fca3255 migrate from req_ctx to msgb
We now generalize the USB communiction and abandon the 'req_ctx'
structure inherited from openpcd.  Instead we use the libosmocore 'msgb'
structure to handle incoming and outgoing USB tranfers.  We also use
linuxlist-based msgb-queues for each endpoint.
2017-05-07 23:09:56 +02:00
Harald Welte
eb81d23a56 import libosmocore msgb handling (and dependencies)
This is supposed to be replaced with upstream libosmocore, as soon as
that has a proper maintained embedded build (again).
2017-05-07 13:18:36 +02:00
Harald Welte
0380d74405 Makefile: remove .p files during 'make clean' 2017-05-07 11:20:40 +02:00
Harald Welte
9457bf765d Makefile: Don't warn about failed inlines 2017-05-07 11:20:24 +02:00
Harald Welte
9ac794c770 Add llist_add_irqsafe() similar to llist_add_tail_irqsafe() 2017-05-07 11:19:45 +02:00
Harald Welte
41eb98b78d board_gpio.gnumeric: Update to QMOD v3 (SIMPRES1/SIMPRES2) 2017-05-05 23:17:31 +02:00
Harald Welte
caca0b1e7a Makefile: Add new 'combined' target to build combined DFU+APP image 2017-05-05 23:17:31 +02:00
Harald Welte
6d1128e9d1 QMOD: Add code to determine SIM Card presence 2017-05-05 23:17:27 +02:00
Harald Welte
7e4390f181 qmod: wwan_led.c: Fix handling of LED for second Modem 2017-05-05 20:58:53 +02:00
Harald Welte
9164a6d335 qmod: Disable various console commands on ST34
A number of commands related to the USB hub and the EEPROM are only
available on ST12, but not on ST34.  Let's remove them if not
applicable.
2017-05-05 20:58:52 +02:00
Harald Welte
2cbc9b29f3 use same USB Product ID for DFU and runtime mode
This was introduced for interoperability with operating systems that
might prefer such setup (I heard that Windows prefers this about a
decade ago, but I don't have any personal experience with it).

However, using different VID/PID between DFU and RT breaks usability of
dfu-util, and I really think this matters much more to our users and
developers.
2017-05-05 00:34:28 +02:00
Harald Welte
8196e4d1be remove old python userspace code
the USB protocol is changing fundamentally anyway, and APDU handling for
card emulation is inside the device, so the old code is incompatible
anyway.
2017-03-08 15:34:23 +01:00
Harald Welte
601e0d3e35 update + split README into general and firmware-specific part 2017-03-08 15:33:33 +01:00
Harald Welte
3d4869cbb4 add missing usbstring.c 2017-03-07 15:51:49 +01:00
Harald Welte
353330dcc7 qmod: Fix polarity of SPDT switch
the default boot state should be to use the local SIM, until the user
changes it (currently only possible via entering '!' or '@' on the
serial console).  The code so far had this completely inverted.
2017-03-07 08:17:19 +01:00
Harald Welte
3bbaba0090 DFU: Disable LED blinking code
Something odd is happening that breaks DFU mode if we blink.  Let's
remove this feature for now.
2017-03-06 22:47:06 +01:00
Harald Welte
e8869fb8ff DFU: Resolve DFU runtime descriptor dynamically from descriptors
We cannoy simply use the DFU runtime descriptor of the DFU mode, but we
have to use the descriptor of the specific currently-selected runtime
configuration.  Let's iterate over the descriptors of a configuration
and find the DFU runtime descriptor in it.
2017-03-06 22:44:42 +01:00
Harald Welte
5b108d8cf0 DFU: initialize g_dfu during real power up
At power-up we need to initialize g_dfu once, to ensure a consistent
state.  Afterwards we want to keep it across (software) reset, but on
power-up the memory would otherwise be filled with random data, causing
issues with detection of DFU/Runtime switching.
2017-03-06 21:51:55 +01:00
Harald Welte
96065cacd7 add simtrace2-list to .gitignore 2017-03-06 21:05:44 +01:00
Harald Welte
1892fc1d7c add udev rules for all (currently) supported devices 2017-03-06 21:03:41 +01:00
Harald Welte
822d66ef69 simtrace2-remsim: Improve support for many interfaces + devices
Rather than using the first available interface on the first available
device, we now have a "simtrace2-list" program that lists all compatible
interfaces on all configurations of all devices on the system
2017-03-06 21:03:31 +01:00
Harald Welte
62bfd8a7a9 simtrace2-remsim: Document and add longopts for UDP forwarding 2017-03-06 20:56:14 +01:00
Harald Welte
b170ea90d3 host: fix some compiler warnings (now that we have -Wall) 2017-03-06 18:59:41 +01:00
Harald Welte
dea64cb746 host: Add "-Wall -g" CFLAGS to get compiler warnings 2017-03-06 18:57:08 +01:00
Harald Welte
2ba03bb9fc simtrace2-remsim: Move away from global variables
When we want to run multiple instances of the card-emulator (e.g. for
multi-modem/multi-card versions of the hardware), the code shouldn't be
using any global variables but have them per-instance.
2017-03-06 18:57:01 +01:00
Harald Welte
abba8a8d85 LED: Introduce LED blinking pattern code
It might be useful to display some different blinking patterns to
indicate specific system state (such as DFU mode vs. regular firmware)
2017-03-06 16:58:00 +01:00
Harald Welte
054216d94d usb: Use different SubClass values for card-emulation and sniffer
This way, host code can dynamically detect which interface supports
which functionality.

The related #defines should be moved to a header file that's shared with
the host application code.
2017-03-06 10:07:17 +01:00
Harald Welte
eab7e456fe USB: Don't specify Class/Subclass 0xff at device level
Actually, at device level we want to specify 0, so we can select
individual Class/Subclass values at Interface values.

Table 9-8 of the USB2 Specification is quite clear about this.
2017-03-06 10:04:56 +01:00
Harald Welte
495a67da7d usb: consistently use named structure initializers 2017-03-06 09:55:37 +01:00
Harald Welte
912b183b29 qmod: Use different Interface Strings for Modem1+2 / Modem 3+4
This makes it obvious in 'lsusb' and to other software on the USB host
which interface is for which of the modems.
2017-03-06 09:28:13 +01:00
Harald Welte
809e5840f9 qmod: Add code to switch between physical and virtual SIM 2017-03-06 09:16:40 +01:00
Harald Welte
0a8306ec69 add gnumeric spreadsheet with GPIO/Pin assignments of all boards
This software is supposed to support a SIMTRACE1 with SAM#, as well
ast the OWHW and QMOD hardware.  Let's compare the GPIO/Pin assignments
of the SAM3 in one shared spreadsheet.
2017-03-05 22:17:28 +01:00
Harald Welte
2bff7cd9c2 tc_etu: Don't confiugre unused TIOA pin of TC0 + TC2
When initializing the TC blocks, let's only configure the GPIO pins TCLK
and TIOB, and not the unused TIOA pin.  That pin is actually used for
(separate) different functions in both qmod and owhw.
2017-03-05 22:15:54 +01:00
Harald Welte
705e899e5f qmod: (re)activate USB port remapping.
The port mapping is now as follows:

* port 1: ST12
* port 2: modem 1
* port 3: modem 2
* port 4: ST34
* port 5: modem 3
* port 6: modem 4
* port 7: daisy-chaining port
2017-03-05 17:10:26 +01:00
Harald Welte
0e2959859a qmod: Replace hand-crafted delay loop with call to mdelay() 2017-03-05 16:48:47 +01:00
Harald Welte
c6e482d581 qmod eeprom/i2c: Re-start watchdog while slow bit-banging 2017-03-05 16:24:29 +01:00
Harald Welte
1776997f8a remove old 'notes' file with german textaul notes 2017-03-05 13:30:32 +01:00
Harald Welte
596e666fa0 fix symlink that caused host programs to not build 2017-03-05 13:28:32 +01:00
Harald Welte
2363fa0327 Generate USB Strings from apps/*/usb_strings.txt files at compile time
This way we can skip the manually-crafted USB string definitions in the
dfu_desc.c and usb.c files.
2017-03-05 10:16:25 +01:00
Harald Welte
1405100dff DFU: Introduce board/app-specific override for booting in DFU mode
Using the USBDFU_OverrideEnterDFU() function, a board/application can
define extra conditions when the system should boot in DFU mode, even if
it was not explicitly switched to DFU mode from the application.

The app/dfu/main.c uses this mechanism to boot into DFU mode if the
stack + reset vector addresses are not plausible (i.e. some random junk
appears to be flashed in the application partition) or if the user
places a jumper accross the RxD+TxD lines of the debug UART.  The idea
is that the system can be recovered by placing this jumper and then
re-installing the application from DFU.
2017-03-04 19:17:27 +01:00
Harald Welte
7214b4747f Make sure to print CPU Reset Cause when starting the software 2017-03-03 19:02:09 +01:00
Harald Welte
45ebe4591a Enable Watchdog (500ms) 2017-03-03 19:01:53 +01:00
Harald Welte
aaba4af46c Use Supply Controller to avoid running SAM3 on voltages lower than 3.0V
Sometimes there is some leakage current via some I/O that's sufficient
to power up the  SAM3S.  Use the supply monitor to make sure the CPU
will be reset (and kept in reset) if the supply voltage is below 3.0V.
2017-03-03 18:48:13 +01:00
Harald Welte
3ecbf678db only simtrace is bus-powered, SAM3 on qmod + owhw are self-powered 2017-03-03 02:10:34 +01:00
Harald Welte
8adf0ac2ce DFU app: call into board_main_top() for qmod related initialization 2017-03-03 01:52:34 +01:00
Harald Welte
f415d7163b Call USBD_Disconnect before software-triggered CPU reset
This makes sure that we'll re-enumerate on the USB, as a CPU reset
apparently doesn't automatically release the pull-up and notify the hub
that we were gone?
2017-03-03 01:51:43 +01:00
Harald Welte
ec0837c463 change from \r\n (CRLF) to \n\r (LFCR)
For some strange reason my output is garbled in both the 'screen' and
'cu' teerminal programs and 'raw' terminal (stty) mode.  I fail to
understand why, but let's simply adjust the code as needed for now.
2017-03-03 01:33:24 +01:00
97 changed files with 6664 additions and 2976 deletions

2
.gitignore vendored
View File

@@ -17,5 +17,7 @@ tags
*.size *.size
*.bin *.bin
*.p *.p
host/simtrace2-list
host/simtrace2-remsim host/simtrace2-remsim
host/simtrace2-remsim-usb2udp host/simtrace2-remsim-usb2udp
usb_strings_generated.h

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "usb_application/pysim"]
path = usb_application/pysim
url = git://git.osmocom.org/pysim

112
README.md
View File

@@ -1,112 +0,0 @@
# SIMtrace v2.0
The SIMtrace software together with the corresponding hardware provides a means to trace the communication between a SIM card and a mobile phone, and intercept it starting with SIMtrace software version 2.0 (together with SIMtrace board version 1.5).
Furthermore, it provides a SIM card emulation and CCID reader mode.
## How to compile
A Makefile is provided. It created an image under bin/project-flash.bin, which can directly be flashed on the board (see section "How to flash").
The level of debug messages can be altered at compile time:
```
$ make TRACE_LEVEL=4
```
Accepted values: 0 (NO_TRACE) to 5 (DEBUG)
## How to flash
For flashing the firmware, there are at least two options.
The first one is using openocd and a JTAG key.
For this option, a JTAG connector has to be soldered onto the board, which is not attached per default.
The Makefile already provides an option for that:
```
$ make program
```
This command will call the following command:
```
$ openocd -f openocd/openocd.cfg -c "init" -c "halt" -c "flash write_bank 0 ./bin/project-flash.bin 0" -c "reset" -c "shutdown"
```
The second option is using rumba for flashing. No further hardware has to be provided for this option.
The software can be obtained with the following shell command:
```
$ git clone git://git.osmocom.org/osmo-sdr.git
```
Flashing the compiled firmware can be done with the following command:
```
$ $OSMO_SDR_DIR/utils/rumba /dev/ttyACM0 flashmcu $FIRMWARE_DIR/bin/project-flash.bin
```
## How to set udev rules
The next step is defining the udev rules for simtrace.
Open the file /etc/udev/rules.d/90-simtrace.rules and enter those four lines:
```
# Temporary VID and PID
SUBSYSTEM=="usb", ATTR{idVendor}=="03eb", ATTR{idProduct}=="6004", MODE="666"
# Future SIMtrace VID and PID
SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="0762)", MODE="666"
```
After reloading the udev rules, SIMtrace should be recognized by the operating system.
## How to use
After flashing the firmware and defining the udev rules, the python program simtrace.py can be used in order to command the board.
First, the configuration has to be set using the -C option, which has to be passed a number determining the mode:
1: Sniffer mode
2: CCID reader mode
3: Mobile phone emulation mode
4: MITM mode
For example, setting the device into MITM mode can be achieved with the following command:
```
$ simtrace.py -C4
```
After setting the configuration, one of the following functionalities can be started:
```
-s, --sniff Sniff communication!
-S, --select_file Transmit SELECT cmd!
-p, --phone Emulates simcard
-m, --mitm Intercept communication (MITM)
```
For example, in order to use simtrace in sniffer mode, the following command can be executed:
```
$ simtrace.py -C1 -s
```
For more information, execute the following command:
```
$ simtrace.py -h
```
## Logging
The Makefile furthermore provides an easy option for reading the log messages.
SIMtrace sends out log messages over the serial interface, using a connector with a 2.5mm jack.
Using a serial to USB converter, the log messages can be read using the following command:
```
$ make log SERIAL=/dev/ttyUSB*
```
If no SERIAL is defined, /dev/ttyUSB0 is taken by default.
## Known issues
* If there is an error, it might result from a missing instruction byte in the list of instructions that expect data from the simcard.
It can be updated in the file in apdu_split.py
Especially, if the sniffer mode works well, but the mitm mode fails, that's a good place to start looking.
The array is of the following form:
`INS_data_expected = [0xC0, 0xB0, 0x12]`
* For interacting with the SIM card (CCID reader and MITM mode), pcscd has to be
started on the computer.
* The maximum operating frequency of the device and hardware is not determined yet.
The function for changing the FIDI is not tested yet because no device could be obtained, which would change the FIDI in the middle of the communication.
Most devices stick with the default FIDI.
* The software assumes a master-slave-protocol: The master sends a command, the slave answers this.
If this premise is not met, the software will not operate properly.
This should be taken into account when programming the Mobile phone emulator or MITM mode.

View File

@@ -95,9 +95,9 @@ C_OSMOCORE = $(notdir $(wildcard libosmocore/source/*.c))
C_LIBCHIP = $(notdir $(wildcard $(AT91LIB)/libchip_sam3s/source/*.c) $(wildcard $(AT91LIB)/libchip_sam3s/cmsis/*.c)) C_LIBCHIP = $(notdir $(wildcard $(AT91LIB)/libchip_sam3s/source/*.c) $(wildcard $(AT91LIB)/libchip_sam3s/cmsis/*.c))
C_LIBUSB = USBDescriptors.c USBRequests.c USBD.c USBDCallbacks.c USBDDriver.c USBDDriverCallbacks.c C_LIBUSB = USBDescriptors.c USBRequests.c USBD.c USBDCallbacks.c USBDDriver.c USBDDriverCallbacks.c
C_LIBUSB_RT = dfu.c dfu_desc.c dfu_runtime.c C_LIBUSB_RT = dfu.c dfu_runtime.c
C_LIBUSB_DFU = dfu.c dfu_desc.c dfu_driver.c C_LIBUSB_DFU = dfu.c dfu_desc.c dfu_driver.c
C_LIBCOMMON = string.c stdio.c fputs.c req_ctx.c ringbuffer.c C_LIBCOMMON = string.c stdio.c fputs.c usb_buf.c ringbuffer.c pseudo_talloc.c host_communication.c
C_BOARD = $(notdir $(wildcard libboard/common/source/*.c)) C_BOARD = $(notdir $(wildcard libboard/common/source/*.c))
C_BOARD += $(notdir $(wildcard libboard/$(BOARD)/source/*.c)) C_BOARD += $(notdir $(wildcard libboard/$(BOARD)/source/*.c))
@@ -139,6 +139,7 @@ INCLUDES += -Ilibboard/common/include -Ilibboard/$(BOARD)/include
INCLUDES += -Ilibcommon/include INCLUDES += -Ilibcommon/include
INCLUDES += -Ilibosmocore/include INCLUDES += -Ilibosmocore/include
INCLUDES += -Isrc_simtrace -Iinclude INCLUDES += -Isrc_simtrace -Iinclude
INCLUDES += -Iapps/$(APP)
CFLAGS += -Wall -Wchar-subscripts -Wcomment -Wimplicit-int #-Wformat=2 CFLAGS += -Wall -Wchar-subscripts -Wcomment -Wimplicit-int #-Wformat=2
CFLAGS += -Werror-implicit-function-declaration -Wmain -Wparentheses CFLAGS += -Werror-implicit-function-declaration -Wmain -Wparentheses
@@ -149,7 +150,7 @@ CFLAGS += -Wsign-compare -Waggregate-return
CFLAGS += -Wformat=0 CFLAGS += -Wformat=0
CFLAGS += -Wmissing-format-attribute -Wno-deprecated-declarations CFLAGS += -Wmissing-format-attribute -Wno-deprecated-declarations
CFLAGS += #-Wpacked CFLAGS += #-Wpacked
CFLAGS += -Wredundant-decls -Wnested-externs -Winline #-Wlong-long CFLAGS += -Wredundant-decls -Wnested-externs #-Winline -Wlong-long
CFLAGS += -Wunreachable-code CFLAGS += -Wunreachable-code
#CFLAGS += -Wcast-align #CFLAGS += -Wcast-align
#CFLAGS += -std=c11 #CFLAGS += -std=c11
@@ -179,11 +180,26 @@ OUTPUT := $(BIN)/$(OUTPUT)
# Rules # Rules
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
all: $(BIN) $(OBJ) $(MEMORIES) all: apps/$(APP)/usb_strings_generated.h $(BIN) $(OBJ) $(MEMORIES)
combined: $(OUTPUT)-combined.bin
$(BIN)/$(BOARD)-dfu-flash-padded.bin: $(BIN)/$(BOARD)-dfu-flash.bin
dd if=/dev/zero bs=16384 count=1 of=$@
dd if=$< conv=notrunc of=$@
$(OUTPUT)-combined.bin: $(BIN)/$(BOARD)-dfu-flash-padded.bin $(OUTPUT)-dfu.bin
cat $^ > $@
$(BIN) $(OBJ): $(BIN) $(OBJ):
mkdir $@ mkdir $@
usbstring/usbstring: usbstring/usbstring.c
gcc $^ -o $@
apps/$(APP)/usb_strings_generated.h: apps/$(APP)/usb_strings.txt usbstring/usbstring
cat $< | usbstring/usbstring > $@
define RULES define RULES
C_OBJECTS_$(1) = $(addprefix $(OBJ)/$(1)_, $(C_OBJECTS)) C_OBJECTS_$(1) = $(addprefix $(OBJ)/$(1)_, $(C_OBJECTS))
ASM_OBJECTS_$(1) = $(addprefix $(OBJ)/$(1)_, $(ASM_OBJECTS)) ASM_OBJECTS_$(1) = $(addprefix $(OBJ)/$(1)_, $(ASM_OBJECTS))
@@ -217,4 +233,4 @@ log:
lsof $(SERIAL) && echo "log is already opened" || ( sed -u "s/\r//" $(SERIAL) | ts ) lsof $(SERIAL) && echo "log is already opened" || ( sed -u "s/\r//" $(SERIAL) | ts )
clean: clean:
-rm -fR $(OBJ)/*.o $(BIN)/*.bin $(BIN)/*.elf $(BIN)/*.elf.txt $(BIN)/*.map $(BIN)/*.lst -rm -fR $(OBJ)/*.o $(BIN)/*.bin $(BIN)/*.elf $(BIN)/*.elf.txt $(BIN)/*.map $(BIN)/*.lst `find . -name \*.p`

View File

@@ -31,3 +31,40 @@ script. The current runtime environments include
A given software build is made for a specific combination of an APP A given software build is made for a specific combination of an APP
running in a certain ENVIRONMENT on a given BOARD. running in a certain ENVIRONMENT on a given BOARD.
A Makefile is provided. It will create output files in the format
bin/$(BOARD)-$(APP)-$(ENV).{elf,bin}
You can specify the APP and BOARD to build when calling make, like
e.g.
* make APP=cardem BOARD=qmod
* make APP=dfu BOARD=qmod
The level of debug messages can be altered at compile time:
```
$ make TRACE_LEVEL=4
```
Accepted values: 0 (NO_TRACE) to 5 (DEBUG)
== Flashing
For flashing the firmware, there are at least two options.
=== Using JTAG + OpenOCD to flash the DFU bootloader
The first one is using openocd and a JTAG key.
For this option, a JTAG connector has to be soldered onto the board, which is not attached per default.
```
$ openocd -f openocd/openocd.cfg -c "init" -c "halt" -c "flash write_bank 0 ./bin/$(BOARD)-dfu-flash.bin 0" -c "reset" -c "shutdown"
```
=== Using bossac to flash the DFU bootloader
The second option is using rumba for flashing. No further hardware has to be provided for this option.
FIXME
=== Using DFU to flash application
FIXME

View File

@@ -1,3 +1,3 @@
C_FILES += $(C_LIBUSB_RT) C_FILES += $(C_LIBUSB_RT)
C_FILES += card_emu.c ccid.c host_communication.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c C_FILES += card_emu.c ccid.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c

View File

@@ -6,7 +6,6 @@
#include "board.h" #include "board.h"
#include "simtrace.h" #include "simtrace.h"
#include "utils.h" #include "utils.h"
#include "req_ctx.h"
#include "osmocom/core/timer.h" #include "osmocom/core/timer.h"
unsigned int g_unique_id[4]; unsigned int g_unique_id[4];
@@ -121,58 +120,61 @@ extern int main(void)
enum confNum last_simtrace_config = simtrace_config; enum confNum last_simtrace_config = simtrace_config;
unsigned int i = 0; unsigned int i = 0;
LED_Configure(LED_NUM_RED); led_init();
LED_Configure(LED_NUM_GREEN); led_blink(LED_RED, BLINK_3O_5F);
LED_Set(LED_NUM_RED);
/* Disable watchdog */ /* Enable watchdog for 500ms, with no window */
WDT_Disable(WDT); WDT_Enable(WDT, WDT_MR_WDRSTEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT |
(WDT_GetPeriod(500) << 16) | WDT_GetPeriod(500));
req_ctx_init();
PIO_InitializeInterrupts(0); PIO_InitializeInterrupts(0);
EEFC_ReadUniqueID(g_unique_id); EEFC_ReadUniqueID(g_unique_id);
printf("\r\n\r\n" printf("\n\r\n\r"
"=============================================================================\r\n" "=============================================================================\n\r"
"SIMtrace2 firmware " GIT_VERSION " (C) 2010-2016 by Harald Welte\r\n" "SIMtrace2 firmware " GIT_VERSION " (C) 2010-2016 by Harald Welte\n\r"
"=============================================================================\r\n"); "=============================================================================\n\r");
TRACE_INFO("Serial Nr. %08x-%08x-%08x-%08x\r\n", TRACE_INFO("Chip ID: 0x%08x (Ext 0x%08x)\n\r", CHIPID->CHIPID_CIDR, CHIPID->CHIPID_EXID);
TRACE_INFO("Serial Nr. %08x-%08x-%08x-%08x\n\r",
g_unique_id[0], g_unique_id[1], g_unique_id[0], g_unique_id[1],
g_unique_id[2], g_unique_id[3]); g_unique_id[2], g_unique_id[3]);
TRACE_INFO("Reset Cause: 0x%x\n\r", (RSTC->RSTC_SR & RSTC_SR_RSTTYP_Msk) >> RSTC_SR_RSTTYP_Pos);
board_main_top(); board_main_top();
TRACE_INFO("USB init...\r\n"); TRACE_INFO("USB init...\n\r");
SIMtrace_USB_Initialize(); SIMtrace_USB_Initialize();
while (USBD_GetState() < USBD_STATE_CONFIGURED) { while (USBD_GetState() < USBD_STATE_CONFIGURED) {
WDT_Restart(WDT);
check_exec_dbg_cmd(); check_exec_dbg_cmd();
#if 0 #if 0
if (i >= MAX_USB_ITER * 3) { if (i >= MAX_USB_ITER * 3) {
TRACE_ERROR("Resetting board (USB could " TRACE_ERROR("Resetting board (USB could "
"not be configured)\r\n"); "not be configured)\n\r");
USBD_Disconnect();
NVIC_SystemReset(); NVIC_SystemReset();
} }
#endif #endif
i++; i++;
} }
TRACE_INFO("calling configure of all configurations...\r\n"); TRACE_INFO("calling configure of all configurations...\n\r");
for (i = 1; i < sizeof(config_func_ptrs) / sizeof(config_func_ptrs[0]); for (i = 1; i < sizeof(config_func_ptrs) / sizeof(config_func_ptrs[0]);
++i) { ++i) {
if (config_func_ptrs[i].configure) if (config_func_ptrs[i].configure)
config_func_ptrs[i].configure(); config_func_ptrs[i].configure();
} }
TRACE_INFO("calling init of config %u...\r\n", simtrace_config); TRACE_INFO("calling init of config %u...\n\r", simtrace_config);
config_func_ptrs[simtrace_config].init(); config_func_ptrs[simtrace_config].init();
last_simtrace_config = simtrace_config; last_simtrace_config = simtrace_config;
TRACE_INFO("entering main loop...\r\n"); TRACE_INFO("entering main loop...\n\r");
while (1) { while (1) {
WDT_Restart(WDT);
#if TRACE_LEVEL >= TRACE_LEVEL_DEBUG #if TRACE_LEVEL >= TRACE_LEVEL_DEBUG
const char rotor[] = { '-', '\\', '|', '/' }; const char rotor[] = { '-', '\\', '|', '/' };
putchar('\b'); putchar('\b');
@@ -188,14 +190,12 @@ extern int main(void)
isUsbConnected = 0; isUsbConnected = 0;
} }
} else if (isUsbConnected == 0) { } else if (isUsbConnected == 0) {
TRACE_INFO("USB is now configured\r\n"); TRACE_INFO("USB is now configured\n\r");
LED_Set(LED_NUM_GREEN);
LED_Clear(LED_NUM_RED);
isUsbConnected = 1; isUsbConnected = 1;
} }
if (last_simtrace_config != simtrace_config) { if (last_simtrace_config != simtrace_config) {
TRACE_INFO("USB config chg %u -> %u\r\n", TRACE_INFO("USB config chg %u -> %u\n\r",
last_simtrace_config, simtrace_config); last_simtrace_config, simtrace_config);
config_func_ptrs[last_simtrace_config].exit(); config_func_ptrs[last_simtrace_config].exit();
config_func_ptrs[simtrace_config].init(); config_func_ptrs[simtrace_config].init();

View File

@@ -0,0 +1,10 @@
sysmocom - s.f.m.c. GmbH
SIMtrace 2 compatible device
SIMtrace Sniffer
SIMtrace CCID
SIMtrace Phone
SIMtrace MITM
CardEmulator Modem 1
CardEmulator Modem 2
CardEmulator Modem 3
CardEmulator Modem 4

View File

@@ -4,6 +4,8 @@
#include "usb/common/dfu/usb_dfu.h" #include "usb/common/dfu/usb_dfu.h"
#include "manifest.h" #include "manifest.h"
#include <osmocom/core/timer.h>
#define ALTIF_RAM 0 #define ALTIF_RAM 0
#define ALTIF_FLASH 1 #define ALTIF_FLASH 1
@@ -99,10 +101,61 @@ int USBDFU_handle_upload(uint8_t altif, unsigned int offset,
/* FIXME: set error codes */ /* FIXME: set error codes */
return -1; return -1;
} }
printf("=%u\r\n", req_len); printf("=%u\n\r", req_len);
return req_len; return req_len;
} }
static int uart_has_loopback_jumper(void)
{
unsigned int i;
const Pin uart_loopback_pins[] = {
{PIO_PA9A_URXD0, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT},
{PIO_PA10A_UTXD0, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
};
/* Configure UART pins as I/O */
PIO_Configure(uart_loopback_pins, PIO_LISTSIZE(uart_loopback_pins));
for (i = 0; i < 10; i++) {
/* Set TxD high; abort if RxD doesn't go high either */
PIO_Set(&uart_loopback_pins[1]);
if (!PIO_Get(&uart_loopback_pins[0]))
return 0;
/* Set TxD low, abort if RxD doesn't go low either */
PIO_Clear(&uart_loopback_pins[1]);
if (PIO_Get(&uart_loopback_pins[0]))
return 0;
}
/* if we reached here, RxD always follows TxD and thus a
* loopback jumper has been placed on RxD/TxD, and we will boot
* into DFU unconditionally */
return 1;
}
/* using this function we can determine if we should enter DFU mode
* during boot, or if we should proceed towards the application/runtime */
int USBDFU_OverrideEnterDFU(void)
{
uint32_t *app_part = (uint32_t *)FLASH_ADDR(0);
/* If the loopback jumper is set, we enter DFU mode */
if (uart_has_loopback_jumper())
return 1;
/* if the first word of the application partition doesn't look
* like a stack pointer (i.e. point to RAM), enter DFU mode */
if ((app_part[0] < IRAM_ADDR) ||
((uint8_t *)app_part[0] > IRAM_END))
return 1;
/* if the second word of the application partition doesn't look
* like a function from flash (reset vector), enter DFU mode */
if (((uint32_t *)app_part[1] < app_part) ||
((uint8_t *)app_part[1] > IFLASH_END))
return 1;
return 0;
}
/* returns '1' in case we should break any endless loop */ /* returns '1' in case we should break any endless loop */
static void check_exec_dbg_cmd(void) static void check_exec_dbg_cmd(void)
@@ -125,42 +178,51 @@ extern int main(void)
{ {
uint8_t isUsbConnected = 0; uint8_t isUsbConnected = 0;
unsigned int i = 0; unsigned int i = 0;
uint32_t reset_cause = (RSTC->RSTC_SR & RSTC_SR_RSTTYP_Msk) >> RSTC_SR_RSTTYP_Pos;
LED_Configure(LED_NUM_RED); #if 0
LED_Configure(LED_NUM_GREEN); led_init();
LED_Set(LED_NUM_RED); led_blink(LED_GREEN, BLINK_3O_30F);
led_blink(LED_RED, BLINK_3O_30F);
#endif
/* Disable watchdog */ /* Enable watchdog for 500ms, with no window */
WDT_Disable(WDT); WDT_Enable(WDT, WDT_MR_WDRSTEN | WDT_MR_WDDBGHLT | WDT_MR_WDIDLEHLT |
(WDT_GetPeriod(500) << 16) | WDT_GetPeriod(500));
//req_ctx_init();
PIO_InitializeInterrupts(0); PIO_InitializeInterrupts(0);
EEFC_ReadUniqueID(g_unique_id); EEFC_ReadUniqueID(g_unique_id);
printf("\r\n\r\n" printf("\n\r\n\r"
"=============================================================================\r\n" "=============================================================================\n\r"
"DFU bootloader %s for board %s (C) 2010-2017 by Harald Welte\r\n" "DFU bootloader %s for board %s (C) 2010-2017 by Harald Welte\n\r"
"=============================================================================\r\n", "=============================================================================\n\r",
manifest_revision, manifest_board); manifest_revision, manifest_board);
TRACE_INFO("Chip ID: 0x%08x (Ext 0x%08x)\r\n", CHIPID->CHIPID_CIDR, CHIPID->CHIPID_EXID); TRACE_INFO("Chip ID: 0x%08x (Ext 0x%08x)\n\r", CHIPID->CHIPID_CIDR, CHIPID->CHIPID_EXID);
TRACE_INFO("Serial Nr. %08x-%08x-%08x-%08x\r\n", TRACE_INFO("Serial Nr. %08x-%08x-%08x-%08x\n\r",
g_unique_id[0], g_unique_id[1], g_unique_id[0], g_unique_id[1],
g_unique_id[2], g_unique_id[3]); g_unique_id[2], g_unique_id[3]);
TRACE_INFO("Reset Cause: 0x%x\n\r", reset_cause);
//board_main_top(); /* clear g_dfu on power-up reset */
if (reset_cause == 0)
memset(g_dfu, 0, sizeof(*g_dfu));
TRACE_INFO("USB init...\r\n"); board_main_top();
TRACE_INFO("USB init...\n\r");
USBDFU_Initialize(&dfu_descriptors); USBDFU_Initialize(&dfu_descriptors);
while (USBD_GetState() < USBD_STATE_CONFIGURED) { while (USBD_GetState() < USBD_STATE_CONFIGURED) {
WDT_Restart(WDT);
check_exec_dbg_cmd(); check_exec_dbg_cmd();
#if 1 #if 1
if (i >= MAX_USB_ITER * 3) { if (i >= MAX_USB_ITER * 3) {
TRACE_ERROR("Resetting board (USB could " TRACE_ERROR("Resetting board (USB could "
"not be configured)\r\n"); "not be configured)\n\r");
USBD_Disconnect();
NVIC_SystemReset(); NVIC_SystemReset();
} }
#endif #endif
@@ -168,16 +230,19 @@ extern int main(void)
} }
FLASHD_Initialize(BOARD_MCK, 1); FLASHD_Initialize(BOARD_MCK, 1);
TRACE_INFO("entering main loop...\r\n"); TRACE_INFO("entering main loop...\n\r");
while (1) { while (1) {
WDT_Restart(WDT);
#if TRACE_LEVEL >= TRACE_LEVEL_DEBUG #if TRACE_LEVEL >= TRACE_LEVEL_DEBUG
const char rotor[] = { '-', '\\', '|', '/' }; const char rotor[] = { '-', '\\', '|', '/' };
putchar('\b'); putchar('\b');
putchar(rotor[i++ % ARRAY_SIZE(rotor)]); putchar(rotor[i++ % ARRAY_SIZE(rotor)]);
#endif #endif
check_exec_dbg_cmd(); check_exec_dbg_cmd();
//osmo_timers_prepare(); #if 0
//osmo_timers_update(); osmo_timers_prepare();
osmo_timers_update();
#endif
if (USBD_GetState() < USBD_STATE_CONFIGURED) { if (USBD_GetState() < USBD_STATE_CONFIGURED) {
@@ -185,9 +250,7 @@ extern int main(void)
isUsbConnected = 0; isUsbConnected = 0;
} }
} else if (isUsbConnected == 0) { } else if (isUsbConnected == 0) {
TRACE_INFO("USB is now configured\r\n"); TRACE_INFO("USB is now configured\n\r");
LED_Set(LED_NUM_GREEN);
LED_Clear(LED_NUM_RED);
isUsbConnected = 1; isUsbConnected = 1;
} }

View File

@@ -0,0 +1,5 @@
sysmocom - s.f.m.c. GmbH
SIMtrace 2 compatible device
DFU (Device Firmare Upgrade)
RAM
Flash (Application Partition)

View File

@@ -1,3 +1,3 @@
C_FILES += $(C_LIBUSB_RT) C_FILES += $(C_LIBUSB_RT)
C_FILES += card_emu.c ccid.c host_communication.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c C_FILES += card_emu.c ccid.c iso7816_4.c iso7816_fidi.c mitm.c mode_cardemu.c simtrace_iso7816.c sniffer.c tc_etu.c usb.c

View File

@@ -154,6 +154,7 @@ extern int main(void)
if (i >= MAX_USB_ITER * 3) { if (i >= MAX_USB_ITER * 3) {
TRACE_ERROR("Resetting board (USB could " TRACE_ERROR("Resetting board (USB could "
"not be configured)\r\n"); "not be configured)\r\n");
USBD_Disconnect();
NVIC_SystemReset(); NVIC_SystemReset();
} }
#endif #endif

View File

@@ -81,7 +81,7 @@ struct hardfault_args {
void hard_fault_handler_c(struct hardfault_args *args) void hard_fault_handler_c(struct hardfault_args *args)
{ {
printf("HardFault\r\n"); printf("\r\nHardFault\r\n");
printf("R0=%08x, R1=%08x, R2=%08x, R3=%08x, R12=%08x\r\n", printf("R0=%08x, R1=%08x, R2=%08x, R3=%08x, R12=%08x\r\n",
args->r0, args->r1, args->r2, args->r3, args->r12); args->r0, args->r1, args->r2, args->r3, args->r12);
printf("LR[R14]=%08x, PC[R15]=%08x, PSR=%08x\r\n", printf("LR[R14]=%08x, PC[R15]=%08x, PSR=%08x\r\n",
@@ -90,6 +90,52 @@ void hard_fault_handler_c(struct hardfault_args *args)
SCB->BFAR, SCB->CFSR, SCB->HFSR); SCB->BFAR, SCB->CFSR, SCB->HFSR);
printf("DFSR=%08x, AFSR=%08x, SHCSR=%08x\r\n", printf("DFSR=%08x, AFSR=%08x, SHCSR=%08x\r\n",
SCB->DFSR, SCB->CFSR, SCB->SHCSR); SCB->DFSR, SCB->CFSR, SCB->SHCSR);
if (SCB->HFSR & 0x40000000)
printf("FORCED ");
if (SCB->HFSR & 0x00000002)
printf("VECTTBL ");
uint32_t ufsr = SCB->CFSR >> 16;
if (ufsr & 0x0200)
printf("DIVBYZERO ");
if (ufsr & 0x0100)
printf("UNALIGNED ");
if (ufsr & 0x0008)
printf("NOCP ");
if (ufsr & 0x0004)
printf("INVPC ");
if (ufsr & 0x0002)
printf("INVSTATE ");
if (ufsr & 0x0001)
printf("UNDEFINSTR ");
uint32_t bfsr = (SCB->CFSR >> 8) & 0xff;
if (bfsr & 0x80)
printf("BFARVALID ");
if (bfsr & 0x10)
printf("STKERR ");
if (bfsr & 0x08)
printf("UNSTKERR ");
if (bfsr & 0x04)
printf("IMPRECISERR ");
if (bfsr & 0x02)
printf("PRECISERR ");
if (bfsr & 0x01)
printf("IBUSERR ");
uint32_t mmfsr = (SCB->CFSR & 0xff);
if (mmfsr & 0x80)
printf("MMARVALID ");
if (mmfsr & 0x10)
printf("MSTKERR ");
if (mmfsr & 0x08)
printf("MUNSTKERR ");
if (mmfsr & 0x02)
printf("DACCVIOL ");
if (mmfsr & 0x01)
printf("IACCVIOL ");
while ( 1 ) ; while ( 1 ) ;
} }

View File

@@ -110,6 +110,7 @@ extern int USBDFU_handle_dnload(uint8_t altif, unsigned int offset,
uint8_t *data, unsigned int len); uint8_t *data, unsigned int len);
extern int USBDFU_handle_upload(uint8_t altif, unsigned int offset, extern int USBDFU_handle_upload(uint8_t altif, unsigned int offset,
uint8_t *data, unsigned int req_len); uint8_t *data, unsigned int req_len);
extern int USBDFU_OverrideEnterDFU(void);
/* function to be called at end of EP0 handler during runtime */ /* function to be called at end of EP0 handler during runtime */
void USBDFU_Runtime_RequestHandler(const USBGenericRequest *request); void USBDFU_Runtime_RequestHandler(const USBGenericRequest *request);

View File

@@ -29,8 +29,8 @@ static const USBDeviceDescriptor fsDevice = {
.bDeviceSubClass = 0, .bDeviceSubClass = 0,
.bDeviceProtocol = 0, .bDeviceProtocol = 0,
.bMaxPacketSize0 = BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), .bMaxPacketSize0 = BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0),
.idVendor = BOARD_USB_VENDOR, .idVendor = BOARD_USB_VENDOR_ID,
.idProduct = BOARD_USB_PRODUCT, .idProduct = BOARD_DFU_USB_PRODUCT_ID,
.bcdDevice = BOARD_USB_RELEASE, .bcdDevice = BOARD_USB_RELEASE,
.iManufacturer = STR_MANUF, .iManufacturer = STR_MANUF,
.iProduct = STR_PROD, .iProduct = STR_PROD,
@@ -85,114 +85,13 @@ const struct dfu_desc dfu_cfg_descriptor = {
.func_dfu = DFU_FUNC_DESC .func_dfu = DFU_FUNC_DESC
}; };
#include "usb_strings_generated.h"
#if 0 #if 0
#include "usb_strings.h"
static const unsigned char *usb_strings[] = {
USB_STRINGS_GENERATED
#ifdef BOARD_USB_SERIAL
NULL
#endif
};
void set_usb_serial_str(const uint8_t *serial_usbstr) void set_usb_serial_str(const uint8_t *serial_usbstr)
{ {
usb_strings[STR_SERIAL] = serial_usbstr; usb_strings[STR_SERIAL] = serial_usbstr;
} }
#else
static const unsigned char langDesc[] = {
USBStringDescriptor_LENGTH(1),
USBGenericDescriptor_STRING,
USBStringDescriptor_ENGLISH_US
};
static const unsigned char manufStringDescriptor[] = {
USBStringDescriptor_LENGTH(24),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('s'),
USBStringDescriptor_UNICODE('y'),
USBStringDescriptor_UNICODE('s'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('o'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('o'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('-'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('s'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE('f'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('G'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('b'),
USBStringDescriptor_UNICODE('H'),
};
static const unsigned char productStringDescriptor[] = {
USBStringDescriptor_LENGTH(10),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('e'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('2'),
};
static const unsigned char configStringDescriptor[] = {
USBStringDescriptor_LENGTH(3),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('D'),
USBStringDescriptor_UNICODE('F'),
USBStringDescriptor_UNICODE('U'),
};
static const unsigned char altRamStringDescriptor[] = {
USBStringDescriptor_LENGTH(3),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('R'),
USBStringDescriptor_UNICODE('A'),
USBStringDescriptor_UNICODE('M'),
};
static const unsigned char altAppStringDescriptor[] = {
USBStringDescriptor_LENGTH(11),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('F'),
USBStringDescriptor_UNICODE('l'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('s'),
USBStringDescriptor_UNICODE('h'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('('),
USBStringDescriptor_UNICODE('A'),
USBStringDescriptor_UNICODE('p'),
USBStringDescriptor_UNICODE('p'),
USBStringDescriptor_UNICODE(')'),
};
/** List of string descriptors used by the device */
static const unsigned char *usb_strings[] = {
langDesc,
[STR_MANUF] = manufStringDescriptor,
[STR_PROD] = productStringDescriptor,
[STR_CONFIG] = configStringDescriptor,
[_STR_FIRST_ALT] = altRamStringDescriptor,
[_STR_FIRST_ALT+1] = altAppStringDescriptor,
};
#endif #endif
static const USBConfigurationDescriptor *conf_desc_arr[] = { static const USBConfigurationDescriptor *conf_desc_arr[] = {

View File

@@ -472,6 +472,14 @@ void USBDFU_SwitchToApp(void)
NVIC_SystemReset(); NVIC_SystemReset();
} }
/* A board can provide a function overriding this, enabling a
* board-specific 'boot into DFU' override, like a specific GPIO that
* needs to be pulled a certain way. */
WEAK int USBDFU_OverrideEnterDFU(void)
{
return 0;
}
void USBDCallbacks_RequestReceived(const USBGenericRequest *request) void USBDCallbacks_RequestReceived(const USBGenericRequest *request)
{ {
USBDFU_DFU_RequestHandler(request); USBDFU_DFU_RequestHandler(request);

View File

@@ -22,6 +22,7 @@
*/ */
#include <board.h> #include <board.h>
#include <assert.h>
#include <core_cm3.h> #include <core_cm3.h>
#include <usb/include/USBD.h> #include <usb/include/USBD.h>
@@ -67,6 +68,26 @@ static void __dfufunc handle_getstate(void)
USBD_Write(0, (char *)&u8, sizeof(u8), NULL, 0); USBD_Write(0, (char *)&u8, sizeof(u8), NULL, 0);
} }
static const uint8_t *get_dfu_func_desc(void)
{
USBDDriver *usbdDriver = USBD_GetDriver();
const USBConfigurationDescriptor *cfg_desc;
const USBGenericDescriptor *gen_desc;
if (USBD_IsHighSpeed())
cfg_desc = &usbdDriver->pDescriptors->pHsConfiguration[usbdDriver->cfgnum];
else
cfg_desc = usbdDriver->pDescriptors->pFsConfiguration[usbdDriver->cfgnum];
for (gen_desc = (const USBGenericDescriptor *) cfg_desc;
(const uint8_t *) gen_desc < (const uint8_t *) cfg_desc + cfg_desc->wTotalLength;
gen_desc = (const USBGenericDescriptor *) ((const uint8_t *)gen_desc + gen_desc->bLength)) {
if (gen_desc->bDescriptorType == USB_DT_DFU)
return (const uint8_t *) gen_desc;
}
return NULL;
}
static void TerminateCtrlInWithNull(void *pArg, static void TerminateCtrlInWithNull(void *pArg,
unsigned char status, unsigned char status,
unsigned long int transferred, unsigned long int transferred,
@@ -100,15 +121,18 @@ void USBDFU_Runtime_RequestHandler(const USBGenericRequest *request)
USBGetDescriptorRequest_GetDescriptorType(request) == USB_DT_DFU) { USBGetDescriptorRequest_GetDescriptorType(request) == USB_DT_DFU) {
uint16_t length = sizeof(struct usb_dfu_func_descriptor); uint16_t length = sizeof(struct usb_dfu_func_descriptor);
const USBDeviceDescriptor *pDevice; const USBDeviceDescriptor *pDevice;
const uint8_t *dfu_func_desc = get_dfu_func_desc();
int terminateWithNull; int terminateWithNull;
ASSERT(dfu_func_desc);
if (USBD_IsHighSpeed()) if (USBD_IsHighSpeed())
pDevice = usbdDriver->pDescriptors->pHsDevice; pDevice = usbdDriver->pDescriptors->pHsDevice;
else else
pDevice = usbdDriver->pDescriptors->pFsDevice; pDevice = usbdDriver->pDescriptors->pFsDevice;
terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0); terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0);
USBD_Write(0, &dfu_cfg_descriptor.func_dfu, length, USBD_Write(0, dfu_func_desc, length,
terminateWithNull ? TerminateCtrlInWithNull : 0, 0); terminateWithNull ? TerminateCtrlInWithNull : 0, 0);
return; return;
} }

View File

@@ -39,11 +39,11 @@
#define BOARD_MCK 48000000 #define BOARD_MCK 48000000
#define LED_RED PIO_PA17 #define PIO_LED_RED PIO_PA17
#define LED_GREEN PIO_PA18 #define PIO_LED_GREEN PIO_PA17
#define PIN_LED_RED {LED_RED, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT} #define PIN_LED_RED {PIO_LED_RED, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
#define PIN_LED_GREEN {LED_GREEN, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT} #define PIN_LED_GREEN {PIO_LED_GREEN, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
#define PINS_LEDS PIN_LED_RED, PIN_LED_GREEN #define PINS_LEDS PIN_LED_RED, PIN_LED_GREEN
#define LED_NUM_RED 0 #define LED_NUM_RED 0
@@ -102,14 +102,14 @@
#define BOARD_USB_ENDPOINTS_MAXPACKETSIZE(i) (((i == 4) || (i == 5))? 512 : 64) #define BOARD_USB_ENDPOINTS_MAXPACKETSIZE(i) (((i == 4) || (i == 5))? 512 : 64)
#define BOARD_USB_ENDPOINTS_BANKS(i) (((i == 0) || (i == 3)) ? 1 : 2) #define BOARD_USB_ENDPOINTS_BANKS(i) (((i == 0) || (i == 3)) ? 1 : 2)
/// USB attributes configuration descriptor (bus or self powered, remote wakeup) #define USB_VENDOR_OPENMOKO 0x1d50
//#define BOARD_USB_BMATTRIBUTES USBConfigurationDescriptor_SELFPOWERED_NORWAKEUP #define USB_PRODUCT_OWHW_SAM3_DFU 0x4001 /* was 0x4000 */
#define BOARD_USB_BMATTRIBUTES USBConfigurationDescriptor_BUSPOWERED_NORWAKEUP #define USB_PRODUCT_OWHW_SAM3 0x4001
//#define BOARD_USB_BMATTRIBUTES USBConfigurationDescriptor_SELFPOWERED_RWAKEUP #define USB_PRODUCT_QMOD_HUB 0x4002
#define USB_PRODUCT_QMOD_SAM3_DFU 0x4004 /* was 0x4003 */
#define BOARD_USB_VENDOR SIMTRACE_VENDOR_ID #define USB_PRODUCT_QMOD_SAM3 0x4004
#define BOARD_USB_PRODUCT SIMTRACE_PRODUCT_ID #define USB_PRODUCT_SIMTRACE2_DFU 0x60e3 /* was 0x60e2 */
#define BOARD_USB_RELEASE 0 #define USB_PRODUCT_SIMTRACE2 0x60e3
#define BOARD_USB_DFU #define BOARD_USB_DFU
#define BOARD_DFU_BOOT_SIZE (16 * 1024) #define BOARD_DFU_BOOT_SIZE (16 * 1024)

View File

@@ -1,72 +1,28 @@
/* ---------------------------------------------------------------------------- #pragma once
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/** enum led {
* \file LED_RED,
* LED_GREEN,
* \section Purpose _NUM_LED
* };
* Small set of functions for simple and portable LED usage.
*
* \section Usage
*
* -# Configure one or more LEDs using LED_Configure and
* LED_ConfigureAll.
* -# Set, clear and toggle LEDs using LED_Set, LED_Clear and
* LED_Toggle.
*
* LEDs are numbered starting from 0; the number of LEDs depend on the
* board being used. All the functions defined here will compile properly
* regardless of whether the LED is defined or not; they will simply
* return 0 when a LED which does not exist is given as an argument.
* Also, these functions take into account how each LED is connected on to
* board; thus, \ref LED_Set might change the level on the corresponding pin
* to 0 or 1, but it will always light the LED on; same thing for the other
* methods.
*/
#ifndef _LED_ enum led_pattern {
#define _LED_ BLINK_ALWAYS_OFF = 0,
BLINK_ALWAYS_ON = 1,
BLINK_3O_5F = 2,
BLINK_3O_30F = 3,
BLINK_3O_1F_3O_30F = 4,
BLINK_3O_1F_3O_1F_3O_30F= 5,
BLINK_200O_F = 6,
BLINK_600O_F = 7,
BLINK_CUSTOM = 8,
_NUM_LED_BLINK
};
#include <stdint.h> void led_init(void);
void led_fini(void);
//------------------------------------------------------------------------------ void led_stop(void);
// Global Functions void led_start(void);
//------------------------------------------------------------------------------
extern uint32_t LED_Configure( uint32_t dwLed ) ;
extern uint32_t LED_Set( uint32_t dwLed ) ;
extern uint32_t LED_Clear( uint32_t dwLed ) ;
extern uint32_t LED_Toggle( uint32_t dwLed ) ;
#endif /* #ifndef LED_H */
void led_blink(enum led led, enum led_pattern blink);
enum led_pattern led_get(enum led led);

View File

@@ -0,0 +1,4 @@
#pragma once
int sim_switch_use_physical(unsigned int nr, int physical);
int sim_switch_init(void);

View File

@@ -157,7 +157,7 @@ void ResetException( void )
/* we are before the text segment has been relocated, so g_dfu is /* we are before the text segment has been relocated, so g_dfu is
* not initialized yet */ * not initialized yet */
g_dfu = &_g_dfu; g_dfu = &_g_dfu;
if (g_dfu->magic != USB_DFU_MAGIC) { if ((g_dfu->magic != USB_DFU_MAGIC) && !USBDFU_OverrideEnterDFU()) {
BootIntoApp(); BootIntoApp();
/* Infinite loop */ /* Infinite loop */
while ( 1 ) ; while ( 1 ) ;

View File

@@ -119,10 +119,17 @@ extern WEAK void LowLevelInit( void )
{ {
uint32_t timeout = 0; uint32_t timeout = 0;
/* Configure the Supply Monitor to reset the CPU in case VDDIO is
* lower than 3.0V. As we run the board on 3.3V, any lower voltage
* might be some kind of leakage that creeps in some way, but is not
* the "official" power supply */
SUPC->SUPC_SMMR = SUPC_SMMR_SMTH_3_0V | SUPC_SMMR_SMSMPL_CSM |
SUPC_SMMR_SMRSTEN_ENABLE;
/* enable both LED and green LED */ /* enable both LED and green LED */
PIOA->PIO_PER |= LED_RED | LED_GREEN; PIOA->PIO_PER |= PIO_LED_RED | PIO_LED_GREEN;
PIOA->PIO_OER |= LED_RED | LED_GREEN; PIOA->PIO_OER |= PIO_LED_RED | PIO_LED_GREEN;
PIOA->PIO_CODR |= LED_RED | LED_GREEN; PIOA->PIO_CODR |= PIO_LED_RED | PIO_LED_GREEN;
/* Set 3 FWS for Embedded Flash Access */ /* Set 3 FWS for Embedded Flash Access */
EFC->EEFC_FMR = EEFC_FMR_FWS(3); EFC->EEFC_FMR = EEFC_FMR_FWS(3);
@@ -162,7 +169,7 @@ extern WEAK void LowLevelInit( void )
#endif #endif
/* disable the red LED after main clock initialization */ /* disable the red LED after main clock initialization */
PIOA->PIO_SODR = LED_RED; PIOA->PIO_SODR = PIO_LED_RED;
/* "switch" to main clock as master clock source (should already be the case */ /* "switch" to main clock as master clock source (should already be the case */
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~(uint32_t)PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK; PMC->PMC_MCKR = (PMC->PMC_MCKR & ~(uint32_t)PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK;

View File

@@ -1,168 +1,258 @@
/* ---------------------------------------------------------------------------- #include <stdint.h>
* ATMEL Microcontroller Software Support #include <string.h>
* ---------------------------------------------------------------------------- #include <assert.h>
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/** #include <osmocom/core/timer.h>
* \file
*/
/*------------------------------------------------------------------------------
* Headers
*------------------------------------------------------------------------------*/
#include "board.h" #include "board.h"
#include "utils.h"
/*------------------------------------------------------------------------------ #include "led.h"
* Local Variables
*------------------------------------------------------------------------------*/
#ifdef PINS_LEDS #ifdef PINS_LEDS
static const Pin pinsLeds[] = { PINS_LEDS } ; static const Pin pinsLeds[] = { PINS_LEDS } ;
static const uint32_t numLeds = PIO_LISTSIZE( pinsLeds ) ;
#endif
/*------------------------------------------------------------------------------ static void led_set(enum led led, int on)
* Global Functions {
*------------------------------------------------------------------------------*/ ASSERT(led < PIO_LISTSIZE(pinsLeds));
/** if (on)
* Configures the pin associated with the given LED number. If the LED does PIO_Set(&pinsLeds[led]);
* not exist on the board, the function does nothing. else
* \param led Number of the LED to configure. PIO_Clear(&pinsLeds[led]);
* \return 1 if the LED exists and has been configured; otherwise 0. }
*/
extern uint32_t LED_Configure( uint32_t dwLed ) /* LED blinking code */
/* a single state in a sequence of blinking */
struct blink_state {
/* duration of the state in ms */
uint16_t duration;
/* bringhtness of LED during the state */
uint8_t on;
} __attribute__((packed));
static const struct blink_state bs_off[] = {
{ 0, 0 }
};
static const struct blink_state bs_on[] = {
{ 0, 1 }
};
static const struct blink_state bs_3on_5off[] = {
{ 300, 1 }, { 500, 0 }
};
static const struct blink_state bs_3on_30off[] = {
{ 300, 1 }, { 3000, 0 }
};
static const struct blink_state bs_3on_1off_3on_30off[] = {
{ 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 }
};
static const struct blink_state bs_3on_1off_3on_1off_3on_30off[] = {
{ 300, 1 }, { 100, 0 }, { 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 }
};
static const struct blink_state bs_200on_off[] = {
{ 20000, 1 }, { 0, 0 },
};
static const struct blink_state bs_600on_off[] = {
{ 60000, 1 }, { 0, 0 },
};
/* a blink pattern is an array of blink_states */
struct blink_pattern {
const struct blink_state *states;
uint16_t size;
};
/* compiled-in default blinking patterns */
static const struct blink_pattern patterns[] = {
[BLINK_ALWAYS_OFF] = {
.states = bs_off,
.size = ARRAY_SIZE(bs_off),
},
[BLINK_ALWAYS_ON] = {
.states = bs_on,
.size = ARRAY_SIZE(bs_on),
},
[BLINK_3O_5F] = {
.states = bs_3on_5off,
.size = ARRAY_SIZE(bs_3on_5off),
},
[BLINK_3O_30F] = {
.states = bs_3on_30off,
.size = ARRAY_SIZE(bs_3on_30off),
},
[BLINK_3O_1F_3O_30F] = {
.states = bs_3on_1off_3on_30off,
.size = ARRAY_SIZE(bs_3on_1off_3on_30off),
},
[BLINK_3O_1F_3O_1F_3O_30F] = {
.states = bs_3on_1off_3on_1off_3on_30off,
.size = ARRAY_SIZE(bs_3on_1off_3on_1off_3on_30off),
},
[BLINK_200O_F] = {
.states = bs_200on_off,
.size = ARRAY_SIZE(bs_200on_off),
},
[BLINK_600O_F] = {
.states = bs_600on_off,
.size = ARRAY_SIZE(bs_600on_off),
},
};
struct led_state {
/* which led are we handling */
enum led led;
/* timer */
struct osmo_timer_list timer;
/* pointer and size of blink array */
const struct blink_pattern *pattern;
unsigned int cur_state;
unsigned int illuminated;
/* static allocated space for custom blinking pattern */
struct blink_pattern pattern_cust;
struct blink_state blink_cust[10];
};
static unsigned int cur_state_inc(struct led_state *ls)
{
ls->cur_state = (ls->cur_state + 1) % ls->pattern->size;
return ls->cur_state;
}
static const struct blink_state *
next_blink_state(struct led_state *ls)
{
return &ls->pattern->states[cur_state_inc(ls)];
}
/* apply the next state to the LED */
static void apply_blinkstate(struct led_state *ls,
const struct blink_state *bs)
{
led_set(ls->led, bs->on);
ls->illuminated = bs->on;
/* re-schedule the timer */
if (bs->duration) {
uint32_t us = bs->duration * 1000;
osmo_timer_schedule(&ls->timer, us / 1000000, us % 1000000);
}
}
static void blink_tmr_cb(void *data)
{
struct led_state *ls = data;
const struct blink_state *next_bs = next_blink_state(ls);
/* apply the next state to the LED */
apply_blinkstate(ls, next_bs);
}
static struct led_state led_state[] = {
[LED_GREEN] = {
.led = LED_GREEN,
.timer.cb = blink_tmr_cb,
.timer.data = &led_state[LED_GREEN],
},
[LED_RED] = {
.led = LED_RED,
.timer.cb = blink_tmr_cb,
.timer.data = &led_state[LED_RED],
},
};
#endif /* PINS_LEDS */
void led_blink(enum led led, enum led_pattern blink)
{ {
#ifdef PINS_LEDS #ifdef PINS_LEDS
// Check that LED exists struct led_state *ls;
if ( dwLed >= numLeds)
{
return 0; if (led >= ARRAY_SIZE(led_state))
} return;
ls = &led_state[led];
// Configure LED /* stop previous blinking, if any */
return ( PIO_Configure( &pinsLeds[dwLed], 1 ) ) ; osmo_timer_del(&ls->timer);
#else led_set(led, 0);
return 0 ; ls->illuminated = 0;
ls->pattern = NULL;
ls->cur_state = 0;
switch (blink) {
case BLINK_CUSTOM:
ls->pattern = &ls->pattern_cust;
break;
default:
if (blink >= ARRAY_SIZE(patterns))
return;
ls->pattern = &patterns[blink];
break;
}
if (ls->pattern && ls->pattern->size > 0)
apply_blinkstate(ls, &ls->pattern->states[0]);
#endif #endif
} }
/** enum led_pattern led_get(enum led led)
* Turns the given LED on if it exists; otherwise does nothing.
* \param led Number of the LED to turn on.
* \return 1 if the LED has been turned on; 0 otherwise.
*/
extern uint32_t LED_Set( uint32_t dwLed )
{ {
#ifdef PINS_LEDS #ifdef PINS_LEDS
/* Check if LED exists */ struct led_state *ls;
if ( dwLed >= numLeds ) unsigned int i;
{
return 0 ;
}
/* Turn LED on */ if (led >= ARRAY_SIZE(led_state))
if ( pinsLeds[dwLed].type == PIO_OUTPUT_0 ) return -1;
{ ls = &led_state[led];
PIO_Set( &pinsLeds[dwLed] ) ; if (ls->pattern == &ls->pattern_cust)
} return BLINK_CUSTOM;
else
{
PIO_Clear( &pinsLeds[dwLed] ) ;
}
return 1 ; for (i = 0; i < ARRAY_SIZE(patterns); i++) {
#else if (ls->pattern == &patterns[i])
return 0 ; return i;
}
#endif
/* default case, shouldn't be reached */
return -1;
}
void led_start(void)
{
led_set(LED_GREEN, led_state[LED_GREEN].illuminated);
led_set(LED_RED, led_state[LED_RED].illuminated);
}
void led_stop(void)
{
led_set(LED_GREEN, 0);
led_set(LED_RED, 0);
}
void led_init(void)
{
#ifdef PINS_LEDS
PIO_Configure(pinsLeds, PIO_LISTSIZE(pinsLeds));
led_set(LED_GREEN, 0);
led_set(LED_RED, 0);
#endif #endif
} }
/** void led_fini(void)
* Turns a LED off.
*
* \param led Number of the LED to turn off.
* \return 1 if the LED has been turned off; 0 otherwise.
*/
extern uint32_t LED_Clear( uint32_t dwLed )
{ {
#ifdef PINS_LEDS #ifdef PINS_LEDS
/* Check if LED exists */ /* we don't actually need to do this, but just in case... */
if ( dwLed >= numLeds ) osmo_timer_del(&led_state[LED_RED].timer);
{ osmo_timer_del(&led_state[LED_GREEN].timer);
return 0 ; led_set(LED_GREEN, 0);
} led_set(LED_RED, 0);
/* Turn LED off */
if ( pinsLeds[dwLed].type == PIO_OUTPUT_0 )
{
PIO_Clear( &pinsLeds[dwLed] ) ;
}
else
{
PIO_Set( &pinsLeds[dwLed] ) ;
}
return 1 ;
#else
return 0 ;
#endif #endif
} }
/**
* Toggles the current state of a LED.
*
* \param led Number of the LED to toggle.
* \return 1 if the LED has been toggled; otherwise 0.
*/
extern uint32_t LED_Toggle( uint32_t dwLed )
{
#ifdef PINS_LEDS
/* Check if LED exists */
if ( dwLed >= numLeds )
{
return 0 ;
}
/* Toggle LED */
if ( PIO_GetOutputDataStatus( &pinsLeds[dwLed] ) )
{
PIO_Clear( &pinsLeds[dwLed] ) ;
}
else
{
PIO_Set( &pinsLeds[dwLed] ) ;
}
return 1 ;
#else
return 0 ;
#endif
}

View File

@@ -0,0 +1,67 @@
/* Code to switch between local (physical) and remote (emulated) SIM */
#include "board.h"
#include "trace.h"
#include "sim_switch.h"
#ifdef PIN_SIM_SWITCH1
static const Pin pin_conn_usim1 = {PIO_PA20, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
#endif
#ifdef PIN_SIM_SWITCH2
static const Pin pin_conn_usim2 = {PIO_PA28, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
#endif
static int initialized = 0;
int sim_switch_use_physical(unsigned int nr, int physical)
{
const Pin *pin;
if (!initialized) {
TRACE_ERROR("Somebody forgot to call sim_switch_init()\r\n");
sim_switch_init();
}
TRACE_INFO("Modem %d: %s SIM\n\r", nr,
physical ? "physical" : "virtual");
switch (nr) {
#ifdef PIN_SIM_SWITCH1
case 0:
pin = &pin_conn_usim1;
break;
#endif
#ifdef PIN_SIM_SWITCH2
case 1:
pin = &pin_conn_usim2;
break;
#endif
default:
TRACE_ERROR("Invalid SIM%u\n\r", nr);
return -1;
}
if (physical) {
TRACE_INFO("%u: Use local/physical SIM\r\n", nr);
PIO_Clear(pin);
} else {
TRACE_INFO("%u: Use remote/emulated SIM\r\n", nr);
PIO_Set(pin);
}
return 0;
}
int sim_switch_init(void)
{
int num_switch = 0;
#ifdef PIN_SIM_SWITCH1
PIO_Configure(&pin_conn_usim1, 1);
num_switch++;
#endif
#ifdef PIN_SIM_SWITCH2
PIO_Configure(&pin_conn_usim2, 1);
num_switch++;
#endif
return num_switch;
}

View File

@@ -41,10 +41,12 @@
#define PINS_CARDSIM { PIN_SET_USIM1_PRES, PIN_SET_USIM2_PRES } #define PINS_CARDSIM { PIN_SET_USIM1_PRES, PIN_SET_USIM2_PRES }
#define SIMTRACE_VENDOR_ID 0x1d50 #define BOARD_USB_BMATTRIBUTES USBConfigurationDescriptor_SELFPOWERED_NORWAKEUP
#define SIMTRACE_PRODUCT_ID 0x60e3 /* FIXME */
#define USB_VENDOR_ID SIMTRACE_VENDOR_ID #define BOARD_USB_VENDOR_ID USB_VENDOR_OPENMOKO
#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID #define BOARD_USB_PRODUCT_ID USB_PRODUCT_OWHW_SAM3
#define BOARD_DFU_USB_PRODUCT_ID USB_PRODUCT_OWHW_SAM3_DFU
#define BOARD_USB_RELEASE 0x010
#define CARDEMU_SECOND_UART #define CARDEMU_SECOND_UART
/* Disable VCC/ADC detection, as OWHWv2 has no ADCVREF */ /* Disable VCC/ADC detection, as OWHWv2 has no ADCVREF */

View File

@@ -28,19 +28,20 @@
#define PIN_USIM1_CLK_TC {PIO_PA29, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} #define PIN_USIM1_CLK_TC {PIO_PA29, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
#define PINS_TC_USIM1 PIN_USIM1_IO_TC, PIN_USIM1_CLK_TC #define PINS_TC_USIM1 PIN_USIM1_IO_TC, PIN_USIM1_CLK_TC
#define PIN_SET_USIM1_PRES {PIO_PA12, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
#define PIN_USIM1_nRST {PIO_PA24, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT} #define PIN_USIM1_nRST {PIO_PA24, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT}
#define PIN_USIM1_VCC {PIO_PB3, PIOB, ID_PIOB, PIO_INPUT, PIO_DEFAULT} #define PIN_USIM1_VCC {PIO_PB3, PIOB, ID_PIOB, PIO_INPUT, PIO_DEFAULT}
#define PIN_SET_USIM2_PRES {PIO_PA14, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
#define PIN_USIM2_nRST {PIO_PA7, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT} #define PIN_USIM2_nRST {PIO_PA7, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT}
#define PIN_USIM2_VCC {PIO_PB2, PIOB, ID_PIOB, PIO_INPUT, PIO_DEFAULT} #define PIN_USIM2_VCC {PIO_PB2, PIOB, ID_PIOB, PIO_INPUT, PIO_DEFAULT}
#define PINS_USIM1 PINS_TC_USIM1, PINS_ISO7816_USIM1, PIN_USIM1_nRST, PIN_SET_USIM1_PRES #define PINS_USIM1 PINS_TC_USIM1, PINS_ISO7816_USIM1, PIN_USIM1_nRST
#define PINS_USIM2 PINS_TC_USIM2, PINS_ISO7816_USIM2, PIN_USIM2_nRST, PIN_SET_USIM2_PRES #define PINS_USIM2 PINS_TC_USIM2, PINS_ISO7816_USIM2, PIN_USIM2_nRST
#define PINS_CARDSIM { PIN_SET_USIM1_PRES, PIN_SET_USIM2_PRES } /* from v3 and onwards only (!) */
#define PIN_DET_USIM1_PRES {PIO_PA12, PIOA, ID_PIOA, PIO_INPUT, PIO_PULLUP | PIO_DEGLITCH | PIO_IT_EDGE}
#define PIN_DET_USIM2_PRES {PIO_PA8, PIOA, ID_PIOA, PIO_INPUT, PIO_PULLUP | PIO_DEGLITCH | PIO_IT_EDGE}
/* only in v2 and lower (!) */
#define PIN_PRTPWR_OVERRIDE {PIO_PA8, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT} #define PIN_PRTPWR_OVERRIDE {PIO_PA8, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_DEFAULT}
/* inputs reading the WWAN LED level */ /* inputs reading the WWAN LED level */
@@ -55,10 +56,16 @@
#define PIN_VERSION_DET {PIO_PA19, PIOA, ID_PIOA, PIO_PERIPH_D, PIO_DEFAULT} #define PIN_VERSION_DET {PIO_PA19, PIOA, ID_PIOA, PIO_PERIPH_D, PIO_DEFAULT}
#define SIMTRACE_VENDOR_ID 0x1d50 /* GPIO towards SPDT switches between real SIM and SAM3 */
#define SIMTRACE_PRODUCT_ID 0x60e3 /* FIXME */ #define PIN_SIM_SWITCH1 {PIO_PA20, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
#define USB_VENDOR_ID SIMTRACE_VENDOR_ID #define PIN_SIM_SWITCH2 {PIO_PA28, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}
#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID
#define BOARD_USB_BMATTRIBUTES USBConfigurationDescriptor_SELFPOWERED_NORWAKEUP
#define BOARD_USB_VENDOR_ID USB_VENDOR_OPENMOKO
#define BOARD_USB_PRODUCT_ID USB_PRODUCT_QMOD_SAM3
#define BOARD_DFU_USB_PRODUCT_ID USB_PRODUCT_QMOD_SAM3_DFU
#define BOARD_USB_RELEASE 0x010
#define CARDEMU_SECOND_UART #define CARDEMU_SECOND_UART
#define DETECT_VCC_BY_ADC #define DETECT_VCC_BY_ADC

View File

@@ -0,0 +1,4 @@
#pragma once
int is_card_present(int port);
int card_present_init(void);

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
int wwan_perst_do_reset(int modem_nr); int wwan_perst_set(int modem_nr, int active);
int wwan_perst_do_reset_pulse(int modem_nr, unsigned int duration_ms);
int wwan_perst_init(void); int wwan_perst_init(void);

View File

@@ -4,11 +4,13 @@
#include "board.h" #include "board.h"
#include "simtrace.h" #include "simtrace.h"
#include "utils.h" #include "utils.h"
#include "req_ctx.h"
#include "wwan_led.h" #include "wwan_led.h"
#include "wwan_perst.h" #include "wwan_perst.h"
#include "sim_switch.h"
#include "boardver_adc.h" #include "boardver_adc.h"
#include "card_pres.h"
#include "osmocom/core/timer.h" #include "osmocom/core/timer.h"
#include "usb_buf.h"
static const Pin pin_hubpwr_override = PIN_PRTPWR_OVERRIDE; static const Pin pin_hubpwr_override = PIN_PRTPWR_OVERRIDE;
static const Pin pin_hub_rst = {PIO_PA13, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}; static const Pin pin_hub_rst = {PIO_PA13, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
@@ -16,6 +18,8 @@ static const Pin pin_1234_detect = {PIO_PA14, PIOA, ID_PIOA, PIO_INPUT, PIO_PULL
static const Pin pin_peer_rst = {PIO_PA0, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}; static const Pin pin_peer_rst = {PIO_PA0, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
static const Pin pin_peer_erase = {PIO_PA11, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT}; static const Pin pin_peer_erase = {PIO_PA11, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT};
/* array of generated USB Strings */
extern unsigned char *usb_strings[];
static int qmod_sam3_is_12(void) static int qmod_sam3_is_12(void)
{ {
@@ -26,7 +30,11 @@ static int qmod_sam3_is_12(void)
} }
const unsigned char __eeprom_bin[256] = { const unsigned char __eeprom_bin[256] = {
0x23, 0x42, 0x17, 0x25, 0x00, 0x00, 0x9b, 0x20, 0x01, 0x00, 0x00, 0x00, 0x32, 0x32, 0x32, 0x32, /* 0x00 - 0x0f */ USB_VENDOR_OPENMOKO & 0xff,
USB_VENDOR_OPENMOKO >> 8,
USB_PRODUCT_QMOD_HUB & 0xff,
USB_PRODUCT_QMOD_HUB >> 8,
0x00, 0x00, 0x9b, 0x20, 0x09, 0x00, 0x00, 0x00, 0x32, 0x32, 0x32, 0x32, /* 0x00 - 0x0f */
0x32, 0x04, 0x09, 0x18, 0x0d, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x6f, 0x00, /* 0x10 - 0x1f */ 0x32, 0x04, 0x09, 0x18, 0x0d, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x6f, 0x00, /* 0x10 - 0x1f */
0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x20, 0x00, 0x73, 0x00, 0x2e, 0x00, /* 0x20 - 0x2f */ 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x20, 0x00, 0x73, 0x00, 0x2e, 0x00, /* 0x20 - 0x2f */
0x66, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x47, 0x00, /* 0x30 - 0x3f */ 0x66, 0x00, 0x2e, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x47, 0x00, /* 0x30 - 0x3f */
@@ -52,13 +60,9 @@ static int write_hub_eeprom(void)
int i; int i;
/* wait */ /* wait */
volatile int v; mdelay(100);
/* 440ns per cycle here */
for (i = 0; i < 1000000; i++) {
v = 0;
}
TRACE_INFO("Writing EEPROM...\r\n"); TRACE_INFO("Writing EEPROM...\n\r");
/* write the EEPROM once */ /* write the EEPROM once */
for (i = 0; i < 256; i++) { for (i = 0; i < 256; i++) {
int rc = eeprom_write_byte(0x50, i, __eeprom_bin[i]); int rc = eeprom_write_byte(0x50, i, __eeprom_bin[i]);
@@ -68,12 +72,12 @@ static int write_hub_eeprom(void)
} }
/* then pursue re-reading it again and again */ /* then pursue re-reading it again and again */
TRACE_INFO("Verifying EEPROM...\r\n"); TRACE_INFO("Verifying EEPROM...\n\r");
for (i = 0; i < 256; i++) { for (i = 0; i < 256; i++) {
int byte = eeprom_read_byte(0x50, i); int byte = eeprom_read_byte(0x50, i);
TRACE_INFO("0x%02x: %02x\r\n", i, byte); TRACE_INFO("0x%02x: %02x\n\r", i, byte);
if (byte != __eeprom_bin[i]) if (byte != __eeprom_bin[i])
TRACE_ERROR("Byte %u is wrong, expected 0x%02x, found 0x%02x\r\n", TRACE_ERROR("Byte %u is wrong, expected 0x%02x, found 0x%02x\n\r",
i, __eeprom_bin[i], byte); i, __eeprom_bin[i], byte);
} }
@@ -83,119 +87,165 @@ static int write_hub_eeprom(void)
return 0; return 0;
} }
/* returns '1' in case we should break any endless loop */ static void board_exec_dbg_cmd_st12only(int ch)
void board_exec_dbg_cmd(int ch)
{ {
uint32_t addr, val; uint32_t addr, val;
/* functions below only work on primary (ST12) */
if (!qmod_sam3_is_12())
return;
switch (ch) { switch (ch) {
case '?':
printf("\t?\thelp\r\n");
printf("\tE\tprogram EEPROM\r\n");
printf("\tR\treset SAM3\r\n");
printf("\tO\tEnable PRTPWR_OVERRIDE\r\n");
printf("\to\tDisable PRTPWR_OVERRIDE\r\n");
printf("\tH\tRelease HUB RESET (high)\r\n");
printf("\th\tAssert HUB RESET (low)\r\n");
printf("\tw\tWrite single byte in EEPROM\r\n");
printf("\tr\tRead single byte from EEPROM\r\n");
printf("\tX\tRelease peer SAM3 from reset\r\n");
printf("\tx\tAssert peer SAM3 reset\r\n");
printf("\tY\tRelease peer SAM3 ERASE signal\r\n");
printf("\ty\tAssert peer SAM3 ERASE signal\r\n");
printf("\tU\tProceed to USB Initialization\r\n");
printf("\t1\tGenerate 1ms reset pulse on WWAN1\r\n");
printf("\t2\tGenerate 1ms reset pulse on WWAN2\r\n");
break;
case 'E': case 'E':
write_hub_eeprom(); write_hub_eeprom();
break; break;
case 'R':
printf("Asking NVIC to reset us\r\n");
NVIC_SystemReset();
break;
case 'O': case 'O':
printf("Setting PRTPWR_OVERRIDE\r\n"); printf("Setting PRTPWR_OVERRIDE\n\r");
PIO_Set(&pin_hubpwr_override); PIO_Set(&pin_hubpwr_override);
break; break;
case 'o': case 'o':
printf("Clearing PRTPWR_OVERRIDE\r\n"); printf("Clearing PRTPWR_OVERRIDE\n\r");
PIO_Clear(&pin_hubpwr_override); PIO_Clear(&pin_hubpwr_override);
break; break;
case 'H': case 'H':
printf("Clearing _HUB_RESET -> HUB_RESET high (inactive)\r\n"); printf("Clearing _HUB_RESET -> HUB_RESET high (inactive)\n\r");
PIO_Clear(&pin_hub_rst); PIO_Clear(&pin_hub_rst);
break; break;
case 'h': case 'h':
/* high level drives transistor -> HUB_RESET low */ /* high level drives transistor -> HUB_RESET low */
printf("Asserting _HUB_RESET -> HUB_RESET low (active)\r\n"); printf("Asserting _HUB_RESET -> HUB_RESET low (active)\n\r");
PIO_Set(&pin_hub_rst); PIO_Set(&pin_hub_rst);
break; break;
case 'w': case 'w':
if (PIO_GetOutputDataStatus(&pin_hub_rst) == 0) if (PIO_GetOutputDataStatus(&pin_hub_rst) == 0)
printf("WARNING: attempting EEPROM access while HUB not in reset\r\n"); printf("WARNING: attempting EEPROM access while HUB not in reset\n\r");
printf("Please enter EEPROM offset:\r\n"); printf("Please enter EEPROM offset:\n\r");
UART_GetIntegerMinMax(&addr, 0, 255); UART_GetIntegerMinMax(&addr, 0, 255);
printf("Please enter EEPROM value:\r\n"); printf("Please enter EEPROM value:\n\r");
UART_GetIntegerMinMax(&val, 0, 255); UART_GetIntegerMinMax(&val, 0, 255);
printf("Writing value 0x%02x to EEPROM offset 0x%02x\r\n", val, addr); printf("Writing value 0x%02x to EEPROM offset 0x%02x\n\r", val, addr);
eeprom_write_byte(0x50, addr, val); eeprom_write_byte(0x50, addr, val);
break; break;
case 'r': case 'r':
printf("Please enter EEPROM offset:\r\n"); printf("Please enter EEPROM offset:\n\r");
UART_GetIntegerMinMax(&addr, 0, 255); UART_GetIntegerMinMax(&addr, 0, 255);
printf("EEPROM[0x%02x] = 0x%02x\r\n", addr, eeprom_read_byte(0x50, addr)); printf("EEPROM[0x%02x] = 0x%02x\n\r", addr, eeprom_read_byte(0x50, addr));
break;
default:
printf("Unknown command '%c'\n\r", ch);
break;
}
}
/* returns '1' in case we should break any endless loop */
void board_exec_dbg_cmd(int ch)
{
switch (ch) {
case '?':
printf("\t?\thelp\n\r");
printf("\tR\treset SAM3\n\r");
if (qmod_sam3_is_12()) {
printf("\tE\tprogram EEPROM\n\r");
printf("\tO\tEnable PRTPWR_OVERRIDE\n\r");
printf("\to\tDisable PRTPWR_OVERRIDE\n\r");
printf("\tH\tRelease HUB RESET (high)\n\r");
printf("\th\tAssert HUB RESET (low)\n\r");
printf("\tw\tWrite single byte in EEPROM\n\r");
printf("\tr\tRead single byte from EEPROM\n\r");
}
printf("\tX\tRelease peer SAM3 from reset\n\r");
printf("\tx\tAssert peer SAM3 reset\n\r");
printf("\tY\tRelease peer SAM3 ERASE signal\n\r");
printf("\ty\tAssert peer SAM3 ERASE signal\n\r");
printf("\tU\tProceed to USB Initialization\n\r");
printf("\t1\tGenerate 1ms reset pulse on WWAN1\n\r");
printf("\t2\tGenerate 1ms reset pulse on WWAN2\n\r");
break;
case 'R':
printf("Asking NVIC to reset us\n\r");
USBD_Disconnect();
NVIC_SystemReset();
break; break;
case 'X': case 'X':
printf("Clearing _SIMTRACExx_RST -> SIMTRACExx_RST high (inactive)\r\n"); printf("Clearing _SIMTRACExx_RST -> SIMTRACExx_RST high (inactive)\n\r");
PIO_Clear(&pin_peer_rst); PIO_Clear(&pin_peer_rst);
break; break;
case 'x': case 'x':
printf("Setting _SIMTRACExx_RST -> SIMTRACExx_RST low (active)\r\n"); printf("Setting _SIMTRACExx_RST -> SIMTRACExx_RST low (active)\n\r");
PIO_Set(&pin_peer_rst); PIO_Set(&pin_peer_rst);
break; break;
case 'Y': case 'Y':
printf("Clearing SIMTRACExx_ERASE (inactive)\r\n"); printf("Clearing SIMTRACExx_ERASE (inactive)\n\r");
PIO_Clear(&pin_peer_erase); PIO_Clear(&pin_peer_erase);
break; break;
case 'y': case 'y':
printf("Seetting SIMTRACExx_ERASE (active)\r\n"); printf("Seetting SIMTRACExx_ERASE (active)\n\r");
PIO_Set(&pin_peer_erase); PIO_Set(&pin_peer_erase);
break; break;
case '1': case '1':
printf("Resetting Modem 1 (of this SAM3)\r\n"); printf("Resetting Modem 1 (of this SAM3)\n\r");
wwan_perst_do_reset(1); wwan_perst_do_reset_pulse(0, 300);
break; break;
case '2': case '2':
printf("Resetting Modem 2 (of this SAM3)\r\n"); printf("Resetting Modem 2 (of this SAM3)\n\r");
wwan_perst_do_reset(2); wwan_perst_do_reset_pulse(1, 300);
break;
case '!':
sim_switch_use_physical(0, 0);
break;
case '@':
sim_switch_use_physical(0, 0);
break; break;
default: default:
printf("Unknown command '%c'\r\n", ch); if (!qmod_sam3_is_12())
printf("Unknown command '%c'\n\r", ch);
else
board_exec_dbg_cmd_st12only(ch);
break; break;
} }
} }
void board_main_top(void) void board_main_top(void)
{ {
#ifndef APPLICATION_dfu
usb_buf_init();
wwan_led_init(); wwan_led_init();
wwan_perst_init(); wwan_perst_init();
sim_switch_init();
#endif
/* set PIN_PRTPWR_OVERRIDE to output-low to avoid the internal /* make sure we can detect whether running in ST12 or ST34 */
* pull-up on the input to keep SIMTRACE12 alive */
PIO_Configure(&pin_hubpwr_override, 1);
PIO_Configure(&pin_hub_rst, 1);
PIO_Configure(&pin_1234_detect, 1); PIO_Configure(&pin_1234_detect, 1);
PIO_Configure(&pin_peer_rst, 1);
PIO_Configure(&pin_peer_erase, 1);
i2c_pin_init();
if (qmod_sam3_is_12()) { if (qmod_sam3_is_12()) {
TRACE_INFO("Detected Quad-Modem ST12\r\n"); /* set PIN_PRTPWR_OVERRIDE to output-low to avoid the internal
* pull-up on the input to keep SIMTRACE12 alive */
PIO_Configure(&pin_hubpwr_override, 1);
PIO_Configure(&pin_hub_rst, 1);
}
PIO_Configure(&pin_peer_rst, 1);
PIO_Configure(&pin_peer_erase, 1);
#ifndef APPLICATION_dfu
i2c_pin_init();
#endif
if (qmod_sam3_is_12()) {
TRACE_INFO("Detected Quad-Modem ST12\n\r");
} else { } else {
TRACE_INFO("Detected Quad-Modem ST34\r\n"); TRACE_INFO("Detected Quad-Modem ST34\n\r");
/* make sure we use the second set of USB Strings
* calling the interfaces "Modem 3" and "Modem 4" rather
* than 1+2 */
usb_strings[7] = usb_strings[9];
usb_strings[8] = usb_strings[10];
} }
/* Obtain the circuit board version (currently just prints voltage */ /* Obtain the circuit board version (currently just prints voltage */
get_board_version_adc(); get_board_version_adc();
#ifndef APPLICATION_dfu
/* Initialize checking for card insert/remove events */
card_present_init();
#endif
} }

View File

@@ -0,0 +1,58 @@
#include <osmocom/core/timer.h>
#include "board.h"
#include "utils.h"
#include "card_pres.h"
#define NUM_CARDPRES 2
#define TIMER_INTERVAL_MS 500
static const Pin pin_cardpres[NUM_CARDPRES] = { PIN_DET_USIM1_PRES, PIN_DET_USIM2_PRES };
static int last_state[NUM_CARDPRES] = { -1, -1 };
static struct osmo_timer_list cardpres_timer;
/* Determine if a SIM card is present in the given slot */
int is_card_present(int port)
{
const Pin *pin;
int present;
if (port < 1 || port > NUM_CARDPRES)
return -1;
pin = &pin_cardpres[port-1];
/* Card present signals are low-active, as we have a switch
* against GND and an internal-pull-up in the SAM3 */
present = PIO_Get(pin) ? 0 : 1;
return present;
}
static void cardpres_tmr_cb(void *data)
{
unsigned int i;
for (i = 1; i <= ARRAY_SIZE(pin_cardpres); i++) {
int state = is_card_present(i);
if (state != last_state[i-1]) {
TRACE_INFO("Card Detect %d Status %d -> %d\r\n", i, last_state[i], state);
/* FIXME: report to USB host */
last_state[i-1] = state;
}
}
osmo_timer_schedule(&cardpres_timer, 0, TIMER_INTERVAL_MS*1000);
}
int card_present_init(void)
{
unsigned int i;
PIO_Configure(pin_cardpres, ARRAY_SIZE(pin_cardpres));
/* start timer */
cardpres_timer.cb = cardpres_tmr_cb;
osmo_timer_schedule(&cardpres_timer, 0, TIMER_INTERVAL_MS*1000);
return 2;
}

View File

@@ -157,6 +157,8 @@ int eeprom_write_byte(uint8_t slave, uint8_t addr, uint8_t byte)
{ {
bool nack; bool nack;
WDT_Restart(WDT);
/* Write slave address */ /* Write slave address */
nack = i2c_write_byte(true, false, slave << 1); nack = i2c_write_byte(true, false, slave << 1);
if (nack) if (nack)
@@ -180,6 +182,8 @@ int eeprom_read_byte(uint8_t slave, uint8_t addr)
{ {
bool nack; bool nack;
WDT_Restart(WDT);
/* dummy write cycle */ /* dummy write cycle */
nack = i2c_write_byte(true, false, slave << 1); nack = i2c_write_byte(true, false, slave << 1);
if (nack) if (nack)

View File

@@ -55,7 +55,7 @@ int wwan_led_active(int wwan)
return -1; return -1;
} }
active = PIO_Get(&pin_wwan1) ? 0 : 1; active = PIO_Get(pin) ? 0 : 1;
return active; return active;
} }

View File

@@ -7,51 +7,85 @@
*/ */
#include "board.h" #include "board.h"
#include "trace.h"
#include "wwan_perst.h" #include "wwan_perst.h"
#include "osmocom/core/timer.h" #include "osmocom/core/timer.h"
#define PERST_DURATION_MS 300 struct wwan_perst {
const Pin pin;
struct osmo_timer_list timer;
};
#ifdef PIN_PERST1 #ifdef PIN_PERST1
static const Pin pin_perst1 = PIN_PERST1; static struct wwan_perst perst1 = {
static struct osmo_timer_list perst1_timer; .pin = PIN_PERST1,
};
#endif #endif
#ifdef PIN_PERST2 #ifdef PIN_PERST2
static const Pin pin_perst2 = PIN_PERST2; static struct wwan_perst perst2 = {
static struct osmo_timer_list perst2_timer; .pin = PIN_PERST2,
};
#endif #endif
static int initialized = 0;
static void perst_tmr_cb(void *data) static void perst_tmr_cb(void *data)
{ {
const Pin *pin = data; struct wwan_perst *perst = data;
/* release the (low-active) reset */ /* release the (low-active) reset */
PIO_Clear(pin); TRACE_INFO("De-asserting modem reset\r\n");
PIO_Clear(&perst->pin);
} }
int wwan_perst_do_reset(int modem_nr) static struct wwan_perst *get_perst_for_modem(int modem_nr)
{ {
const Pin *pin; if (!initialized) {
struct osmo_timer_list *tmr; TRACE_ERROR("Somebody forgot to call wwan_perst_init()\r\n");
wwan_perst_init();
}
switch (modem_nr) { switch (modem_nr) {
#ifdef PIN_PERST1 #ifdef PIN_PERST1
case 1: case 0:
pin = &pin_perst1; return &perst1;
tmr = &perst1_timer;
break;
#endif #endif
#ifdef PIN_PERST2 #ifdef PIN_PERST2
case 2: case 1:
pin = &pin_perst2; return &perst2;
tmr = &perst2_timer;
break;
#endif #endif
default: default:
return -1; return NULL;
}
}
int wwan_perst_do_reset_pulse(int modem_nr, unsigned int duration_ms)
{
struct wwan_perst *perst = get_perst_for_modem(modem_nr);
if (!perst)
return -1;
TRACE_INFO("%u: Asserting modem reset\r\n", modem_nr);
PIO_Set(&perst->pin);
osmo_timer_schedule(&perst->timer, duration_ms/1000, (duration_ms%1000)*1000);
return 0;
}
int wwan_perst_set(int modem_nr, int active)
{
struct wwan_perst *perst = get_perst_for_modem(modem_nr);
if (!perst)
return -1;
osmo_timer_del(&perst->timer);
if (active) {
TRACE_INFO("%u: Asserting modem reset\r\n", modem_nr);
PIO_Set(&perst->pin);
} else {
TRACE_INFO("%u: De-asserting modem reset\r\n", modem_nr);
PIO_Clear(&perst->pin);
} }
PIO_Set(pin);
osmo_timer_schedule(tmr, PERST_DURATION_MS/1000, (PERST_DURATION_MS%1000)*1000);
return 0; return 0;
} }
@@ -60,16 +94,16 @@ int wwan_perst_init(void)
{ {
int num_perst = 0; int num_perst = 0;
#ifdef PIN_PERST1 #ifdef PIN_PERST1
PIO_Configure(&pin_perst1, 1); PIO_Configure(&perst1.pin, 1);
perst1_timer.cb = perst_tmr_cb; perst1.timer.cb = perst_tmr_cb;
perst1_timer.data = (void *) &pin_perst1; perst1.timer.data = (void *) &perst1;
num_perst++; num_perst++;
#endif #endif
#ifdef PIN_PERST2 #ifdef PIN_PERST2
PIO_Configure(&pin_perst2, 1); PIO_Configure(&perst2.pin, 1);
perst2_timer.cb = perst_tmr_cb; perst2.timer.cb = perst_tmr_cb;
perst2_timer.data = (void *) &pin_perst2; perst2.timer.data = (void *) &perst2;
num_perst++; num_perst++;
#endif #endif
return num_perst; return num_perst;

View File

@@ -74,10 +74,12 @@
/// SPI chip select 0 pin definition (PA11). /// SPI chip select 0 pin definition (PA11).
#define PIN_SPI_NPCS0 {1 << 11, PIOA, PIOA, PIO_PERIPH_A, PIO_DEFAULT} #define PIN_SPI_NPCS0 {1 << 11, PIOA, PIOA, PIO_PERIPH_A, PIO_DEFAULT}
#define SIMTRACE_VENDOR_ID 0x1d50 #define BOARD_USB_BMATTRIBUTES USBConfigurationDescriptor_BUSPOWERED_NORWAKEUP
#define SIMTRACE_PRODUCT_ID 0x60e3
#define USB_VENDOR_ID SIMTRACE_VENDOR_ID #define BOARD_USB_VENDOR_ID USB_VENDOR_OPENMOKO
#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID #define BOARD_USB_PRODUCT_ID USB_PRODUCT_SIMTRACE2
#define BOARD_DFU_USB_PRODUCT_ID USB_PRODUCT_SIMTRACE2_DFU
#define BOARD_USB_RELEASE 0x000
#define HAVE_SNIFFER #define HAVE_SNIFFER
#define HAVE_CCID #define HAVE_CCID

View File

@@ -89,7 +89,7 @@
/// \param condition Condition to verify. /// \param condition Condition to verify.
#define ASSERT(condition) { \ #define ASSERT(condition) { \
if (!(condition)) { \ if (!(condition)) { \
printf("-F- ASSERT: %s %s:%d\r\n", #condition, __BASE_FILE__, __LINE__); \ printf("-F- ASSERT: %s %s:%d\n\r", #condition, __BASE_FILE__, __LINE__); \
while (1); \ while (1); \
} \ } \
} }

View File

@@ -10,7 +10,8 @@ enum card_io {
CARD_IO_CLK, CARD_IO_CLK,
}; };
struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan); struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan,
uint8_t in_ep, uint8_t irq_ep);
/* process a single byte received from the reader */ /* process a single byte received from the reader */
void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte); void card_emu_process_rx_byte(struct card_handle *ch, uint8_t byte);
@@ -25,7 +26,6 @@ void card_emu_io_statechg(struct card_handle *ch, enum card_io io, int active);
int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len); int card_emu_set_atr(struct card_handle *ch, const uint8_t *atr, uint8_t len);
struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch); struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch);
struct llist_head *card_emu_get_usb_tx_queue(struct card_handle *ch);
void card_emu_have_new_uart_tx(struct card_handle *ch); void card_emu_have_new_uart_tx(struct card_handle *ch);
void card_emu_report_status(struct card_handle *ch); void card_emu_report_status(struct card_handle *ch);

View File

@@ -1,135 +0,0 @@
#pragma once
/* Smart Card Emulation USB protocol */
/* (C) 2015 by Harald Welte <hwelte@hmw-consulting.de>
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdint.h>
/* DT = Device Terminated. DO = Device Originated */
enum cardemu_usb_msg_type {
/* Bulk out pipe */
CEMU_USB_MSGT_DT_TX_DATA, /* TPDU Date */
CEMU_USB_MSGT_DT_SET_ATR, /* Set the ATR stored in simulator */
CEMU_USB_MSGT_DT_GET_STATS, /* request DO_STATS */
CEMU_USB_MSGT_DT_GET_STATUS, /* request DO_STATUS */
CEMU_USB_MSGT_DT_CARDINSERT, /* insert/remove card */
/* Bulk in pipe */
CEMU_USB_MSGT_DO_RX_DATA, /* TPDU data */
CEMU_USB_MSGT_DO_STATUS, /* Status information */
CEMU_USB_MSGT_DO_STATS, /* Statistics */
CEMU_USB_MSGT_DO_PTS, /* Information about PTS */
CEMU_USB_MSGT_DO_ERROR, /* Error message */
};
/* generic header, shared by all messages */
struct cardemu_usb_msg_hdr {
uint8_t msg_type; /* enum cardemu_usb_msg_type */
uint8_t seq_nr; /* sequence number */
uint16_t msg_len; /* length of message including hdr */
uint8_t data[0];
} __attribute__ ((packed));
/* indicates a TPDU header is present in this message */
#define CEMU_DATA_F_TPDU_HDR 0x00000001
/* indicates last part of transmission in this direction */
#define CEMU_DATA_F_FINAL 0x00000002
/* incdicates a PB is present and we should continue with TX */
#define CEMU_DATA_F_PB_AND_TX 0x00000004
/* incdicates a PB is present and we should continue with RX */
#define CEMU_DATA_F_PB_AND_RX 0x00000008
/* CEMU_USB_MSGT_DT_CARDINSERT */
struct cardemu_usb_msg_cardinsert {
struct cardemu_usb_msg_hdr hdr;
uint8_t card_insert;
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DT_SET_ATR */
struct cardemu_usb_msg_set_atr {
struct cardemu_usb_msg_hdr hdr;
uint8_t atr_len;
/* variable-length ATR data */
uint8_t atr[0];
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DT_TX_DATA */
struct cardemu_usb_msg_tx_data {
struct cardemu_usb_msg_hdr hdr;
uint32_t flags;
uint16_t data_len;
/* variable-length TPDU data */
uint8_t data[0];
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DO_RX_DATA */
struct cardemu_usb_msg_rx_data {
struct cardemu_usb_msg_hdr hdr;
uint32_t flags;
uint16_t data_len;
/* variable-length TPDU data */
uint8_t data[0];
} __attribute__ ((packed));
#define CEMU_STATUS_F_VCC_PRESENT 0x00000001
#define CEMU_STATUS_F_CLK_ACTIVE 0x00000002
#define CEMU_STATUS_F_RCEMU_ACTIVE 0x00000004
#define CEMU_STATUS_F_CARD_INSERT 0x00000008
#define CEMU_STATUS_F_RESET_ACTIVE 0x00000010
/* CEMU_USB_MSGT_DO_STATUS */
struct cardemu_usb_msg_status {
struct cardemu_usb_msg_hdr hdr;
uint32_t flags;
/* phone-applied target voltage in mV */
uint16_t voltage_mv;
/* Fi/Di related information */
uint8_t fi;
uint8_t di;
uint8_t wi;
uint32_t waiting_time;
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DO_PTS */
struct cardemu_usb_msg_pts_info {
struct cardemu_usb_msg_hdr hdr;
uint8_t pts_len;
/* PTS request as sent from reader */
uint8_t req[6];
/* PTS response as sent by card */
uint8_t resp[6];
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DO_ERROR */
struct cardemu_usb_msg_error {
struct cardemu_usb_msg_hdr hdr;
uint8_t severity;
uint8_t subsystem;
uint16_t code;
uint8_t msg_len;
/* human-readable error message */
uint8_t msg[0];
} __attribute__ ((packed));
static inline void cardemu_hdr_set(struct cardemu_usb_msg_hdr *hdr, uint16_t msgt)
{
memset(hdr, 0, sizeof(*hdr));
hdr->msg_type = msgt;
}

View File

@@ -2,6 +2,14 @@
#include "osmocom/core/linuxlist.h" #include "osmocom/core/linuxlist.h"
static inline void llist_add_irqsafe(struct llist_head *_new,
struct llist_head *head)
{
__disable_irq();
llist_add(_new, head);
__enable_irq();
}
static inline void llist_add_tail_irqsafe(struct llist_head *_new, static inline void llist_add_tail_irqsafe(struct llist_head *_new,
struct llist_head *head) struct llist_head *head)
{ {

View File

@@ -1,61 +0,0 @@
#pragma once
#define RCTX_SIZE_LARGE 960
#define RCTX_SIZE_SMALL 320
#define MAX_HDRSIZE sizeof(struct openpcd_hdr)
#include <stdint.h>
#include "osmocom/core/linuxlist.h"
#define __ramfunc
enum req_ctx_state {
/* free to be allocated */
RCTX_S_FREE,
/* USB -> UART */
/* In USB driver, waiting for data from host */
RCTX_S_USB_RX_BUSY,
/* somewhere in the main loop */
RCTX_S_MAIN_PROCESSING,
/* pending (in queue) for transmission on UART */
RCTX_S_UART_TX_PENDING,
/* currently in active transmission on UART */
RCTX_S_UART_TX_BUSY,
/* UART -> USB */
/* currently in active reception on UART */
RCTX_S_UART_RX_BUSY,
/* pending (in queue) for transmission over USB to host */
RCTX_S_USB_TX_PENDING,
/* currently in transmission over USB to host */
RCTX_S_USB_TX_BUSY,
/* number of states */
RCTX_STATE_COUNT
};
struct req_ctx {
/* if this req_ctx is on a queue... */
struct llist_head list;
uint32_t ep;
/* enum req_ctx_state */
volatile uint32_t state;
/* size of th 'data' buffer */
uint16_t size;
/* total number of used bytes in buffer */
uint16_t tot_len;
/* index into the buffer, user specific */
uint16_t idx;
/* actual data buffer */
uint8_t *data;
};
extern void req_ctx_init(void);
extern struct req_ctx __ramfunc *req_ctx_find_get(int large, uint32_t old_state, uint32_t new_state);
extern struct req_ctx *req_ctx_find_busy(void);
extern void req_ctx_set_state(struct req_ctx *ctx, uint32_t new_state);
extern void req_ctx_put(struct req_ctx *ctx);
extern uint8_t req_ctx_num(struct req_ctx *ctx);
unsigned int req_ctx_count(uint32_t state);

View File

@@ -113,8 +113,4 @@ extern void mode_cardemu_usart1_irq(void);
void Timer_Init( void ); void Timer_Init( void );
void TC0_Counter_Reset( void ); void TC0_Counter_Reset( void );
struct llist_head;
int usb_refill_to_host(struct llist_head *queue, uint32_t ep);
int usb_refill_from_host(struct llist_head *queue, int ep);
#endif /* SIMTRACE_H */ #endif /* SIMTRACE_H */

View File

@@ -0,0 +1,278 @@
#pragma once
#include <stdint.h>
/* SIMtrace2 USB protocol */
/* (C) 2015-2017 by Harald Welte <hwelte@hmw-consulting.de>
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/***********************************************************************
* COMMON HEADER
***********************************************************************/
enum simtrace_msg_class {
SIMTRACE_MSGC_GENERIC = 0,
/* Card Emulation / Forwarding */
SIMTRACE_MSGC_CARDEM,
/* Modem Control (if modem is attached next to device */
SIMTRACE_MSGC_MODEM,
/* SIM protocol tracing */
SIMTRACE_MSGC_TRACE,
/* first vendor-specific request */
_SIMTRACE_MGSC_VENDOR_FIRST = 127,
};
enum simtrace_msg_type_generic {
/* Generic Error Message */
SIMTRACE_CMD_DO_ERROR = 0,
/* Request/Response for simtrace_board_info */
SIMTRACE_CMD_BD_BOARD_INFO,
};
/* SIMTRACE_MSGC_CARDEM */
enum simtrace_msg_type_cardem {
/* TPDU Data to be transmitted to phone */
SIMTRACE_MSGT_DT_CEMU_TX_DATA = 1,
/* Set the ATR to be returned at phone-SIM reset */
SIMTRACE_MSGT_DT_CEMU_SET_ATR,
/* Get Statistics Request / Response */
SIMTRACE_MSGT_BD_CEMU_STATS,
/* Get Status Request / Response */
SIMTRACE_MSGT_BD_CEMU_STATUS,
/* Request / Confirm emulated card insert */
SIMTRACE_MSGT_DT_CEMU_CARDINSERT,
/* TPDU Data received from phomne */
SIMTRACE_MSGT_DO_CEMU_RX_DATA,
/* Indicate PTS request from phone */
SIMTRACE_MSGT_DO_CEMU_PTS,
};
/* SIMTRACE_MSGC_MODEM */
enum simtrace_msg_type_modem {
/* Modem Control: Reset an attached modem */
SIMTRACE_MSGT_DT_MODEM_RESET = 1,
/* Modem Control: Select local / remote SIM */
SIMTRACE_MSGT_DT_MODEM_SIM_SELECT,
/* Modem Control: Status (WWAN LED, SIM Presence) */
SIMTRACE_MSGT_BD_MODEM_STATUS,
};
/* SIMTRACE_MSGC_TRACE */
enum simtrace_msg_type_trace {
/* FIXME */
_dummy,
};
/* common message header */
struct simtrace_msg_hdr {
uint8_t msg_class; /* simtrace_msg_class */
uint8_t msg_type; /* simtrace_msg_type_xxx */
uint8_t seq_nr;
uint8_t slot_nr; /* SIM slot number */
uint16_t _reserved;
uint16_t msg_len; /* length including header */
uint8_t payload[0];
} __attribute__ ((packed));
/***********************************************************************
* CARD EMULATOR / FORWARDER
***********************************************************************/
/* generic capabilities */
enum simtrace_capability_generic {
/* compatible with 5V SIM card interface */
SIMTRACE_CAP_VOLT_5V,
/* compatible with 3.3V SIM card interface */
SIMTRACE_CAP_VOLT_3V3,
/* compatible with 1.8V SIM card interface */
SIMTRACE_CAP_VOLT_1V8,
/* Has LED1 */
SIMTRACE_CAP_LED_1,
/* Has LED2 */
SIMTRACE_CAP_LED_2,
/* Has Single-Pole Dual-Throw (local/remote SIM */
SIMTRACE_CAP_SPDT,
/* Has Bus-Switch (trace / MITM) */
SIMTRACE_CAP_BUS_SWITCH,
/* Can read VSIM via ADC */
SIMTRACE_CAP_VSIM_ADC,
/* Can read temperature via ADC */
SIMTRACE_CAP_TEMP_ADC,
/* Supports DFU for firmware update */
SIMTRACE_CAP_DFU,
/* Supports Ctrl EP command for erasing flash / return to SAM-BA */
SIMTRACE_CAP_ERASE_FLASH,
/* Can read the status of card insert contact */
SIMTRACE_CAP_READ_CARD_DET,
/* Can control the status of a simulated card insert */
SIMTRACE_CAP_ASSERT_CARD_DET,
/* Can toggle the hardware reset of an attached modem */
SIMTRACE_CAP_ASSERT_MODEM_RST,
};
/* vendor-specific capabilities of sysmoocm devices */
enum simtrace_capability_vendor {
/* Can erase a peer SAM3 controller */
SIMTRACE_CAP_SYSMO_QMOD_ERASE_PEER,
/* Can read/write an attached EEPROM */
SIMTRACE_CAP_SYSMO_QMOD_RW_EEPROM,
/* can reset an attached USB hub */
SIMTRACE_CAP_SYSMO_QMOD_RESET_HUB,
};
/* SIMTRACE_CMD_BD_BOARD_INFO */
struct simtrace_board_info {
struct {
char manufacturer[32];
char model[32];
char version[32];
} hardware;
struct {
/* who provided this software? */
char provider[32];
/* name of software image */
char name[32];
/* (git) version at build time */
char version[32];
/* built on which machine? */
char buildhost[32];
/* CRC-32 over software image */
uint32_t crc;
} software;
struct {
/* Maximum baud rate supported */
uint32_t max_baud_rate;
} speed;
/* number of bytes of generic capability bit-mask */
uint8_t cap_generic_bytes;
/* number of bytes of vendor capability bit-mask */
uint8_t cap_vendor_bytes;
uint8_t data[0];
/* cap_generic + cap_vendor */
} __attribute__ ((packed));
/***********************************************************************
* CARD EMULATOR / FORWARDER
***********************************************************************/
/* indicates a TPDU header is present in this message */
#define CEMU_DATA_F_TPDU_HDR 0x00000001
/* indicates last part of transmission in this direction */
#define CEMU_DATA_F_FINAL 0x00000002
/* incdicates a PB is present and we should continue with TX */
#define CEMU_DATA_F_PB_AND_TX 0x00000004
/* incdicates a PB is present and we should continue with RX */
#define CEMU_DATA_F_PB_AND_RX 0x00000008
/* CEMU_USB_MSGT_DT_CARDINSERT */
struct cardemu_usb_msg_cardinsert {
uint8_t card_insert;
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DT_SET_ATR */
struct cardemu_usb_msg_set_atr {
uint8_t atr_len;
/* variable-length ATR data */
uint8_t atr[0];
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DT_TX_DATA */
struct cardemu_usb_msg_tx_data {
uint32_t flags;
uint16_t data_len;
/* variable-length TPDU data */
uint8_t data[0];
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DO_RX_DATA */
struct cardemu_usb_msg_rx_data {
uint32_t flags;
uint16_t data_len;
/* variable-length TPDU data */
uint8_t data[0];
} __attribute__ ((packed));
#define CEMU_STATUS_F_VCC_PRESENT 0x00000001
#define CEMU_STATUS_F_CLK_ACTIVE 0x00000002
#define CEMU_STATUS_F_RCEMU_ACTIVE 0x00000004
#define CEMU_STATUS_F_CARD_INSERT 0x00000008
#define CEMU_STATUS_F_RESET_ACTIVE 0x00000010
/* CEMU_USB_MSGT_DO_STATUS */
struct cardemu_usb_msg_status {
uint32_t flags;
/* phone-applied target voltage in mV */
uint16_t voltage_mv;
/* Fi/Di related information */
uint8_t fi;
uint8_t di;
uint8_t wi;
uint32_t waiting_time;
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DO_PTS */
struct cardemu_usb_msg_pts_info {
uint8_t pts_len;
/* PTS request as sent from reader */
uint8_t req[6];
/* PTS response as sent by card */
uint8_t resp[6];
} __attribute__ ((packed));
/* CEMU_USB_MSGT_DO_ERROR */
struct cardemu_usb_msg_error {
uint8_t severity;
uint8_t subsystem;
uint16_t code;
uint8_t msg_len;
/* human-readable error message */
uint8_t msg[0];
} __attribute__ ((packed));
/***********************************************************************
* MODEM CONTROL
***********************************************************************/
/* SIMTRACE_MSGT_DT_MODEM_RESET */
struct st_modem_reset {
/* 0: de-assert reset, 1: assert reset, 2: poulse reset */
uint8_t asserted;
/* if above is '2', duration of pulse in ms */
uint16_t pulse_duration_msec;
} __attribute__((packed));
/* SIMTRACE_MSGT_DT_MODEM_SIM_SELECT */
struct st_modem_sim_select {
/* remote (1), local (0) */
uint8_t remote_sim;
} __attribute__((packed));
/* SIMTRACE_MSGT_BD_MODEM_STATUS */
#define ST_MDM_STS_BIT_WWAN_LED (1 << 0)
#define ST_MDM_STS_BIT_CARD_INSERTED (1 << 1)
struct st_modem_status {
/* bit-field of supported status bits */
uint8_t supported_mask;
/* bit-field of current status bits */
uint8_t status_mask;
/* bit-field of changed status bits */
uint8_t changed_mask;
} __attribute__((packed));

View File

@@ -0,0 +1,25 @@
#pragma once
#include <stdlib.h>
#include <stdarg.h>
/* minimalistic emulation of core talloc API functions used by msgb.c */
#define __TALLOC_STRING_LINE1__(s) #s
#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s)
#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__)
#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
void *_talloc_zero(const void *ctx, size_t size, const char *name);
#define talloc_free(ctx) _talloc_free(ctx, __location__)
int _talloc_free(void *ptr, const char *location);
/* Unsupported! */
#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
void *talloc_named_const(const void *context, size_t size, const char *name);
void talloc_set_name_const(const void *ptr, const char *name);
char *talloc_strdup(const void *t, const char *p);
void *talloc_pool(const void *context, size_t size);

View File

@@ -0,0 +1,28 @@
#pragma once
#include "osmocom/core/linuxlist.h"
#include "osmocom/core/msgb.h"
/* buffered USB endpoint (with queue of msgb) */
struct usb_buffered_ep {
/* endpoint number */
uint8_t ep;
/* OUT endpoint (1) or IN/IRQ (0)? */
uint8_t out_from_host;
/* currently any transfer in progress? */
volatile uint32_t in_progress;
/* Tx queue (IN) / Rx queue (OUT) */
struct llist_head queue;
};
struct msgb *usb_buf_alloc(uint8_t ep);
void usb_buf_free(struct msgb *msg);
int usb_buf_submit(struct msgb *msg);
struct llist_head *usb_get_queue(uint8_t ep);
int usb_drain_queue(uint8_t ep);
void usb_buf_init(void);
struct usb_buffered_ep *usb_get_buf_ep(uint8_t ep);
int usb_refill_to_host(uint8_t ep);
int usb_refill_from_host(uint8_t ep);

View File

@@ -1,5 +1,5 @@
/* ISO7816-3 state machine for the card side */ /* ISO7816-3 state machine for the card side */
/* (C) 2010-2015 by Harald Welte <hwelte@hmw-consulting.de> /* (C) 2010-2017 by Harald Welte <hwelte@hmw-consulting.de>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,9 +31,10 @@
#include "iso7816_fidi.h" #include "iso7816_fidi.h"
#include "tc_etu.h" #include "tc_etu.h"
#include "card_emu.h" #include "card_emu.h"
#include "req_ctx.h" #include "simtrace_prot.h"
#include "cardemu_prot.h" #include "usb_buf.h"
#include "osmocom/core/linuxlist.h" #include "osmocom/core/linuxlist.h"
#include "osmocom/core/msgb.h"
#define NUM_SLOTS 2 #define NUM_SLOTS 2
@@ -114,6 +115,9 @@ struct card_handle {
uint8_t tc_chan; /* TC channel number */ uint8_t tc_chan; /* TC channel number */
uint8_t uart_chan; /* UART channel */ uint8_t uart_chan; /* UART channel */
uint8_t in_ep; /* USB IN EP */
uint8_t irq_ep; /* USB IN EP */
uint32_t waiting_time; /* in clocks */ uint32_t waiting_time; /* in clocks */
/* ATR state machine */ /* ATR state machine */
@@ -138,10 +142,9 @@ struct card_handle {
uint8_t hdr[5]; /* CLA INS P1 P2 P3 */ uint8_t hdr[5]; /* CLA INS P1 P2 P3 */
} tpdu; } tpdu;
struct req_ctx *uart_rx_ctx; /* UART RX -> USB TX */ struct msgb *uart_rx_msg; /* UART RX -> USB TX */
struct req_ctx *uart_tx_ctx; /* USB RX -> UART TX */ struct msgb *uart_tx_msg; /* USB RX -> UART TX */
struct llist_head usb_tx_queue;
struct llist_head uart_tx_queue; struct llist_head uart_tx_queue;
struct { struct {
@@ -151,11 +154,6 @@ struct card_handle {
} stats; } stats;
}; };
struct llist_head *card_emu_get_usb_tx_queue(struct card_handle *ch)
{
return &ch->usb_tx_queue;
}
struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch) struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch)
{ {
return &ch->uart_tx_queue; return &ch->uart_tx_queue;
@@ -164,26 +162,54 @@ struct llist_head *card_emu_get_uart_tx_queue(struct card_handle *ch)
static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts); static void set_tpdu_state(struct card_handle *ch, enum tpdu_state new_ts);
static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss); static void set_pts_state(struct card_handle *ch, enum pts_state new_ptss);
/* update simtrace header msg_len and submit USB buffer */
void usb_buf_upd_len_and_submit(struct msgb *msg)
{
struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *) msg->l1h;
sh->msg_len = msgb_length(msg);
usb_buf_submit(msg);
}
/* Allocate USB buffer and push + initialize simtrace_msg_hdr */
struct msgb *usb_buf_alloc_st(uint8_t ep, uint8_t msg_class, uint8_t msg_type)
{
struct msgb *msg;
struct simtrace_msg_hdr *sh;
msg = usb_buf_alloc(ep);
if (!msg)
return NULL;
msg->l1h = msgb_put(msg, sizeof(*sh));
sh = (struct simtrace_msg_hdr *) msg->l1h;
memset(sh, 0, sizeof(*sh));
sh->msg_class = msg_class;
sh->msg_type = msg_type;
msg->l2h = msg->l1h + sizeof(*sh);
return msg;
}
/* Update cardemu_usb_msg_rx_data length + submit bufffer */
static void flush_rx_buffer(struct card_handle *ch) static void flush_rx_buffer(struct card_handle *ch)
{ {
struct req_ctx *rctx; struct msgb *msg;
struct cardemu_usb_msg_rx_data *rd; struct cardemu_usb_msg_rx_data *rd;
uint32_t data_len;
rctx = ch->uart_rx_ctx; msg = ch->uart_rx_msg;
if (!rctx) if (!msg)
return; return;
ch->uart_rx_ctx = NULL; ch->uart_rx_msg = NULL;
/* store length of data payload fild in header */ /* store length of data payload fild in header */
rd = (struct cardemu_usb_msg_rx_data *) rctx->data; rd = (struct cardemu_usb_msg_rx_data *) msg->l2h;
rd->data_len = rctx->idx; rd->data_len = msgb_l2len(msg) - sizeof(*rd);
rd->hdr.msg_len = sizeof(*rd) + rd->data_len;
req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING); usb_buf_upd_len_and_submit(msg);
/* no need for irqsafe operation, as the usb_tx_queue is
* processed only by the main loop context */
llist_add_tail(&rctx->list, &ch->usb_tx_queue);
} }
/* convert a non-contiguous PTS request/responsei into a contiguous /* convert a non-contiguous PTS request/responsei into a contiguous
@@ -223,23 +249,18 @@ static uint8_t csum_pts(const uint8_t *in)
static void flush_pts(struct card_handle *ch) static void flush_pts(struct card_handle *ch)
{ {
struct req_ctx *rctx; struct msgb *msg;
struct cardemu_usb_msg_pts_info *ptsi; struct cardemu_usb_msg_pts_info *ptsi;
rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY); msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DO_CEMU_PTS);
if (!rctx) if (!msg)
return; return;
ptsi = (struct cardemu_usb_msg_pts_info *) rctx->data; ptsi = (struct cardemu_usb_msg_pts_info *) msgb_put(msg, sizeof(*ptsi));
ptsi->hdr.msg_type = CEMU_USB_MSGT_DO_PTS;
ptsi->hdr.msg_len = sizeof(*ptsi);
ptsi->pts_len = serialize_pts(ptsi->req, ch->pts.req); ptsi->pts_len = serialize_pts(ptsi->req, ch->pts.req);
serialize_pts(ptsi->resp, ch->pts.resp); serialize_pts(ptsi->resp, ch->pts.resp);
req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING); usb_buf_upd_len_and_submit(msg);
/* no need for irqsafe operation, as the usb_tx_queue is
* processed only by the main loop context */
llist_add_tail(&rctx->list, &ch->usb_tx_queue);
} }
static void emu_update_fidi(struct card_handle *ch) static void emu_update_fidi(struct card_handle *ch)
@@ -502,37 +523,33 @@ static unsigned int t0_num_data_bytes(uint8_t p3, int reader_to_card)
/* add a just-received TPDU byte (from reader) to USB buffer */ /* add a just-received TPDU byte (from reader) to USB buffer */
static void add_tpdu_byte(struct card_handle *ch, uint8_t byte) static void add_tpdu_byte(struct card_handle *ch, uint8_t byte)
{ {
struct req_ctx *rctx; struct msgb *msg;
struct cardemu_usb_msg_rx_data *rd; struct cardemu_usb_msg_rx_data *rd;
unsigned int num_data_bytes = t0_num_data_bytes(ch->tpdu.hdr[_P3], 0); unsigned int num_data_bytes = t0_num_data_bytes(ch->tpdu.hdr[_P3], 0);
/* ensure we have a buffer */ /* ensure we have a buffer */
if (!ch->uart_rx_ctx) { if (!ch->uart_rx_msg) {
rctx = ch->uart_rx_ctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY); msg = ch->uart_rx_msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM,
if (!ch->uart_rx_ctx) { SIMTRACE_MSGT_DO_CEMU_RX_DATA);
if (!ch->uart_rx_msg) {
TRACE_ERROR("%u: Received UART byte but ENOMEM\r\n", TRACE_ERROR("%u: Received UART byte but ENOMEM\r\n",
ch->num); ch->num);
return; return;
} }
rd = (struct cardemu_usb_msg_rx_data *) ch->uart_rx_ctx->data; msgb_put(msg, sizeof(*rd));
cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DO_RX_DATA);
rctx->tot_len = sizeof(*rd);
rctx->idx = 0;
} else } else
rctx = ch->uart_rx_ctx; msg = ch->uart_rx_msg;
rd = (struct cardemu_usb_msg_rx_data *) rctx->data; rd = (struct cardemu_usb_msg_rx_data *) msg->l2h;
msgb_put_u8(msg, byte);
rd->data[rctx->idx++] = byte;
rctx->tot_len++;
/* check if the buffer is full. If so, send it */ /* check if the buffer is full. If so, send it */
if (rctx->tot_len >= sizeof(*rd) + num_data_bytes) { if (msgb_l2len(msg) >= sizeof(*rd) + num_data_bytes) {
rd->flags |= CEMU_DATA_F_FINAL; rd->flags |= CEMU_DATA_F_FINAL;
flush_rx_buffer(ch); flush_rx_buffer(ch);
/* We need to transmit the SW now, */ /* We need to transmit the SW now, */
set_tpdu_state(ch, TPDU_S_WAIT_TX); set_tpdu_state(ch, TPDU_S_WAIT_TX);
} else if (rctx->tot_len >= rctx->size) } else if (msgb_tailroom(msg) <= 0)
flush_rx_buffer(ch); flush_rx_buffer(ch);
} }
@@ -590,8 +607,9 @@ static enum tpdu_state next_tpdu_state(struct card_handle *ch)
static void send_tpdu_header(struct card_handle *ch) static void send_tpdu_header(struct card_handle *ch)
{ {
struct req_ctx *rctx; struct msgb *msg;
struct cardemu_usb_msg_rx_data *rd; struct cardemu_usb_msg_rx_data *rd;
uint8_t *cur;
TRACE_INFO("%u: %s: %02x %02x %02x %02x %02x\r\n", TRACE_INFO("%u: %s: %02x %02x %02x %02x %02x\r\n",
ch->num, __func__, ch->num, __func__,
@@ -600,32 +618,30 @@ static void send_tpdu_header(struct card_handle *ch)
ch->tpdu.hdr[4]); ch->tpdu.hdr[4]);
/* if we already/still have a context, send it off */ /* if we already/still have a context, send it off */
if (ch->uart_rx_ctx) { if (ch->uart_rx_msg) {
TRACE_DEBUG("%u: have old buffer\r\n", ch->num); TRACE_DEBUG("%u: have old buffer\r\n", ch->num);
if (ch->uart_rx_ctx->idx) { if (msgb_l2len(ch->uart_rx_msg)) {
TRACE_DEBUG("%u: flushing old buffer\r\n", ch->num); TRACE_DEBUG("%u: flushing old buffer\r\n", ch->num);
flush_rx_buffer(ch); flush_rx_buffer(ch);
} }
} else {
TRACE_DEBUG("%u: allocating new buffer\r\n", ch->num);
/* ensure we have a new buffer */
ch->uart_rx_ctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY);
if (!ch->uart_rx_ctx) {
TRACE_ERROR("%u: %s: ENOMEM\r\n", ch->num, __func__);
return;
}
} }
rctx = ch->uart_rx_ctx; TRACE_DEBUG("%u: allocating new buffer\r\n", ch->num);
rd = (struct cardemu_usb_msg_rx_data *) rctx->data; /* ensure we have a new buffer */
ch->uart_rx_msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM,
SIMTRACE_MSGT_DO_CEMU_RX_DATA);
if (!ch->uart_rx_msg) {
TRACE_ERROR("%u: %s: ENOMEM\r\n", ch->num, __func__);
return;
}
msg = ch->uart_rx_msg;
rd = (struct cardemu_usb_msg_rx_data *) msgb_put(msg, sizeof(*rd));
/* initializ header */ /* initialize header */
cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DO_RX_DATA);
rd->flags = CEMU_DATA_F_TPDU_HDR; rd->flags = CEMU_DATA_F_TPDU_HDR;
rctx->tot_len = sizeof(*rd) + sizeof(ch->tpdu.hdr);
rctx->idx = sizeof(ch->tpdu.hdr);
/* copy TPDU header to data field */ /* copy TPDU header to data field */
memcpy(rd->data, ch->tpdu.hdr, sizeof(ch->tpdu.hdr)); cur = msgb_put(msg, sizeof(ch->tpdu.hdr));
memcpy(cur, ch->tpdu.hdr, sizeof(ch->tpdu.hdr));
/* rd->data_len is set in flush_rx_buffer() */ /* rd->data_len is set in flush_rx_buffer() */
flush_rx_buffer(ch); flush_rx_buffer(ch);
@@ -674,33 +690,31 @@ process_byte_tpdu(struct card_handle *ch, uint8_t byte)
/* tx a single byte to be transmitted to the reader */ /* tx a single byte to be transmitted to the reader */
static int tx_byte_tpdu(struct card_handle *ch) static int tx_byte_tpdu(struct card_handle *ch)
{ {
struct req_ctx *rctx; struct msgb *msg;
struct cardemu_usb_msg_tx_data *td; struct cardemu_usb_msg_tx_data *td;
uint8_t byte; uint8_t byte;
/* ensure we are aware of any data that might be pending for /* ensure we are aware of any data that might be pending for
* transmit */ * transmit */
if (!ch->uart_tx_ctx) { if (!ch->uart_tx_msg) {
/* uart_tx_queue is filled from main loop, so no need /* uart_tx_queue is filled from main loop, so no need
* for irq-safe operations */ * for irq-safe operations */
if (llist_empty(&ch->uart_tx_queue)) if (llist_empty(&ch->uart_tx_queue))
return 0; return 0;
/* dequeue first at head */ /* dequeue first at head */
ch->uart_tx_ctx = llist_entry(ch->uart_tx_queue.next, ch->uart_tx_msg = msgb_dequeue(&ch->uart_tx_queue);
struct req_ctx, list); ch->uart_tx_msg->l1h = ch->uart_tx_msg->head;
llist_del(&ch->uart_tx_ctx->list); ch->uart_tx_msg->l2h = ch->uart_tx_msg->l1h + sizeof(struct simtrace_msg_hdr);
req_ctx_set_state(ch->uart_tx_ctx, RCTX_S_UART_TX_BUSY); msg = ch->uart_tx_msg;
/* remove the header */
/* start with index zero */ msgb_pull(msg, sizeof(struct simtrace_msg_hdr) + sizeof(*td));
ch->uart_tx_ctx->idx = 0;
} }
rctx = ch->uart_tx_ctx; msg = ch->uart_tx_msg;
td = (struct cardemu_usb_msg_tx_data *) rctx->data; td = (struct cardemu_usb_msg_tx_data *) msg->l2h;
/* take the next pending byte out of the rctx */ /* take the next pending byte out of the msgb */
byte = td->data[rctx->idx++]; byte = msgb_pull_u8(msg);
card_emu_uart_tx(ch->uart_chan, byte); card_emu_uart_tx(ch->uart_chan, byte);
@@ -719,8 +733,7 @@ static int tx_byte_tpdu(struct card_handle *ch)
} }
/* check if the buffer has now been fully transmitted */ /* check if the buffer has now been fully transmitted */
if ((rctx->idx >= td->data_len) || if (msgb_length(msg) == 0) {
(td->data + rctx->idx >= rctx->data + rctx->tot_len)) {
if (td->flags & CEMU_DATA_F_PB_AND_RX) { if (td->flags & CEMU_DATA_F_PB_AND_RX) {
/* we have just sent the procedure byte and now /* we have just sent the procedure byte and now
* need to continue receiving */ * need to continue receiving */
@@ -733,8 +746,8 @@ static int tx_byte_tpdu(struct card_handle *ch)
card_set_state(ch, ISO_S_WAIT_TPDU); card_set_state(ch, ISO_S_WAIT_TPDU);
} }
} }
req_ctx_set_state(rctx, RCTX_S_FREE); usb_buf_free(msg);
ch->uart_tx_ctx = NULL; ch->uart_tx_msg = NULL;
} }
return 1; return 1;
@@ -836,17 +849,15 @@ void card_emu_have_new_uart_tx(struct card_handle *ch)
void card_emu_report_status(struct card_handle *ch) void card_emu_report_status(struct card_handle *ch)
{ {
struct req_ctx *rctx; struct msgb *msg;
struct cardemu_usb_msg_status *sts; struct cardemu_usb_msg_status *sts;
rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_UART_RX_BUSY); msg = usb_buf_alloc_st(ch->in_ep, SIMTRACE_MSGC_CARDEM,
if (!rctx) SIMTRACE_MSGT_BD_CEMU_STATUS);
if (!msg)
return; return;
rctx->tot_len = sizeof(*sts); sts = (struct cardemu_usb_msg_status *) msgb_put(msg, sizeof(*sts));
sts = (struct cardemu_usb_msg_status *)rctx->data;
sts->hdr.msg_type = CEMU_USB_MSGT_DO_STATUS;
sts->hdr.msg_len = sizeof(*sts);
sts->flags = 0; sts->flags = 0;
if (ch->vcc_active) if (ch->vcc_active)
sts->flags |= CEMU_STATUS_F_VCC_PRESENT; sts->flags |= CEMU_STATUS_F_VCC_PRESENT;
@@ -860,8 +871,7 @@ void card_emu_report_status(struct card_handle *ch)
sts->wi = ch->wi; sts->wi = ch->wi;
sts->waiting_time = ch->waiting_time; sts->waiting_time = ch->waiting_time;
llist_add_tail(&rctx->list, &ch->usb_tx_queue); usb_buf_upd_len_and_submit(msg);
req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING);
} }
/* hardware driver informs us that a card I/O signal has changed */ /* hardware driver informs us that a card I/O signal has changed */
@@ -957,7 +967,8 @@ static const uint8_t default_atr[] = { 0x3B, 0x02, 0x14, 0x50 };
static struct card_handle card_handles[NUM_SLOTS]; static struct card_handle card_handles[NUM_SLOTS];
struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan) struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uart_chan,
uint8_t in_ep, uint8_t irq_ep)
{ {
struct card_handle *ch; struct card_handle *ch;
@@ -968,11 +979,12 @@ struct card_handle *card_emu_init(uint8_t slot_num, uint8_t tc_chan, uint8_t uar
memset(ch, 0, sizeof(*ch)); memset(ch, 0, sizeof(*ch));
INIT_LLIST_HEAD(&ch->usb_tx_queue);
INIT_LLIST_HEAD(&ch->uart_tx_queue); INIT_LLIST_HEAD(&ch->uart_tx_queue);
/* initialize the card_handle with reasonabe defaults */ /* initialize the card_handle with reasonabe defaults */
ch->num = slot_num; ch->num = slot_num;
ch->irq_ep = irq_ep;
ch->in_ep = in_ep;
ch->state = ISO_S_WAIT_POWER; ch->state = ISO_S_WAIT_POWER;
ch->vcc_active = 0; ch->vcc_active = 0;
ch->in_reset = 1; ch->in_reset = 1;

View File

@@ -1,126 +1,168 @@
//#define TRACE_LEVEL 6 #include "board.h"
#include "llist_irqsafe.h"
#include "usb_buf.h"
#include "osmocom/core/linuxlist.h"
#include "osmocom/core/msgb.h"
#include <errno.h> #include <errno.h>
#include "board.h" /***********************************************************************
#include "req_ctx.h" * USBD Integration API
#include "osmocom/core/linuxlist.h" ***********************************************************************/
#include "llist_irqsafe.h"
static volatile uint32_t usbep_in_progress[BOARD_USB_NUMENDPOINTS];
/* call-back after (successful?) transfer of a buffer */ /* call-back after (successful?) transfer of a buffer */
static void usb_write_cb(uint8_t *arg, uint8_t status, uint32_t transferred, static void usb_write_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
uint32_t remaining) uint32_t remaining)
{ {
struct req_ctx *rctx = (struct req_ctx *) arg; struct msgb *msg = (struct msgb *) arg;
struct usb_buffered_ep *bep = msg->dst;
TRACE_DEBUG("%s (EP=%u)\r\n", __func__, rctx->ep); TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
__disable_irq(); __disable_irq();
usbep_in_progress[rctx->ep]--; bep->in_progress--;
__enable_irq(); __enable_irq();
TRACE_DEBUG("%u: in_progress=%d\n", rctx->ep, usbep_in_progress[rctx->ep]); TRACE_DEBUG("%u: in_progress=%d\n", bep->ep, bep->in_progress);
if (status != USBD_STATUS_SUCCESS) if (status != USBD_STATUS_SUCCESS)
TRACE_ERROR("%s error, status=%d\n", __func__, status); TRACE_ERROR("%s error, status=%d\n", __func__, status);
/* release request contxt to pool */ usb_buf_free(msg);
req_ctx_set_state(rctx, RCTX_S_FREE);
} }
int usb_refill_to_host(struct llist_head *queue, uint32_t ep) int usb_refill_to_host(uint8_t ep)
{ {
struct req_ctx *rctx; struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
struct msgb *msg;
int rc; int rc;
#if 0
if (bep->out_from_host) {
TRACE_ERROR("EP 0x%02x is not IN\r\n", bep->ep);
return -EINVAL;
}
#endif
__disable_irq(); __disable_irq();
if (usbep_in_progress[ep]) { if (bep->in_progress) {
__enable_irq(); __enable_irq();
return 0; return 0;
} }
if (llist_empty(queue)) { if (llist_empty(&bep->queue)) {
__enable_irq(); __enable_irq();
return 0; return 0;
} }
usbep_in_progress[ep]++; bep->in_progress++;
rctx = llist_entry(queue->next, struct req_ctx, list); msg = msgb_dequeue(&bep->queue);
llist_del(&rctx->list);
__enable_irq(); __enable_irq();
TRACE_DEBUG("%u: in_progress=%d\n", ep, usbep_in_progress[ep]); TRACE_DEBUG("%s (EP=0x%02x), in_progress=%d\r\n", __func__, ep, bep->in_progress);
TRACE_DEBUG("%s (EP=%u)\r\n", __func__, ep);
req_ctx_set_state(rctx, RCTX_S_USB_TX_BUSY); msg->dst = bep;
rctx->ep = ep;
rc = USBD_Write(ep, rctx->data, rctx->tot_len, rc = USBD_Write(ep, msgb_data(msg), msgb_length(msg),
(TransferCallback) &usb_write_cb, rctx); (TransferCallback) &usb_write_cb, msg);
if (rc != USBD_STATUS_SUCCESS) { if (rc != USBD_STATUS_SUCCESS) {
TRACE_ERROR("%s error %x\n", __func__, rc); TRACE_ERROR("%s error %x\n", __func__, rc);
req_ctx_set_state(rctx, RCTX_S_USB_TX_PENDING); /* re-insert to head of queue */
llist_add_irqsafe(&msg->list, &bep->queue);
__disable_irq(); __disable_irq();
usbep_in_progress[ep]--; bep->in_progress--;
__enable_irq(); __enable_irq();
TRACE_DEBUG("%u: in_progress=%d\n", ep, usbep_in_progress[ep]); TRACE_DEBUG("%02x: in_progress=%d\n", bep->ep, bep->in_progress);
return 0; return 0;
} }
return 1; return 1;
} }
/* call-back after (successful?) transfer of a buffer */
static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred, static void usb_read_cb(uint8_t *arg, uint8_t status, uint32_t transferred,
uint32_t remaining) uint32_t remaining)
{ {
struct req_ctx *rctx = (struct req_ctx *) arg; struct msgb *msg = (struct msgb *) arg;
struct llist_head *queue = (struct llist_head *) usbep_in_progress[rctx->ep]; struct usb_buffered_ep *bep = msg->dst;
TRACE_DEBUG("%s (EP=%u, len=%u, q=%p)\r\n", __func__, TRACE_DEBUG("%s (EP=%u, len=%u, q=%p)\r\n", __func__,
rctx->ep, transferred, queue); bep->ep, transferred, &bep->queue);
usbep_in_progress[rctx->ep] = 0; bep->in_progress = 0;
if (status != USBD_STATUS_SUCCESS) { if (status != USBD_STATUS_SUCCESS) {
TRACE_ERROR("%s error, status=%d\n", __func__, status); TRACE_ERROR("%s error, status=%d\n", __func__, status);
/* release request contxt to pool */ usb_buf_free(msg);
req_ctx_put(rctx);
return; return;
} }
rctx->tot_len = transferred; msgb_put(msg, transferred);
req_ctx_set_state(rctx, RCTX_S_MAIN_PROCESSING); llist_add_tail_irqsafe(&msg->list, &bep->queue);
llist_add_tail_irqsafe(&rctx->list, queue);
} }
int usb_refill_from_host(struct llist_head *queue, int ep) int usb_refill_from_host(uint8_t ep)
{ {
struct req_ctx *rctx; struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
struct msgb *msg;
int rc; int rc;
if (usbep_in_progress[ep]) #if 0
if (!bep->out_from_host) {
TRACE_ERROR("EP 0x%02x is not OUT\r\n", bep->ep);
return -EINVAL;
}
#endif
if (bep->in_progress)
return 0; return 0;
TRACE_DEBUG("%s (EP=%u)\r\n", __func__, ep); TRACE_DEBUG("%s (EP=0x%02x)\r\n", __func__, bep->ep);
rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_USB_RX_BUSY); msg = usb_buf_alloc(bep->ep);
if (!rctx) if (!msg)
return -ENOMEM; return -ENOMEM;
msg->dst = bep;
msg->l1h = msg->head;
rctx->ep = ep; bep->in_progress = 1;
usbep_in_progress[ep] = (uint32_t) queue;
rc = USBD_Read(ep, rctx->data, rctx->size,
(TransferCallback) &usb_read_cb, rctx);
rc = USBD_Read(ep, msg->head, msgb_tailroom(msg),
(TransferCallback) &usb_read_cb, msg);
if (rc != USBD_STATUS_SUCCESS) { if (rc != USBD_STATUS_SUCCESS) {
TRACE_ERROR("%s error %x\n", __func__, rc); TRACE_ERROR("%s error %s\n", __func__, rc);
req_ctx_put(rctx); usb_buf_free(msg);
usbep_in_progress[ep] = 0; bep->in_progress = 0;
return 0;
} }
return 1; return 1;
} }
int usb_drain_queue(uint8_t ep)
{
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
struct msgb *msg;
int ret = 0;
/* wait until no transfers are in progress anymore and block
* further interrupts */
while (1) {
__disable_irq();
if (!bep->in_progress) {
break;
}
__enable_irq();
/* retry */
}
/* free all queued msgbs */
while ((msg = msgb_dequeue(&bep->queue))) {
usb_buf_free(msg);
ret++;
}
/* re-enable interrupts and return number of free'd msgbs */
__enable_irq();
return ret;
}

View File

@@ -7,13 +7,18 @@
#include "iso7816_fidi.h" #include "iso7816_fidi.h"
#include "utils.h" #include "utils.h"
#include "osmocom/core/linuxlist.h" #include "osmocom/core/linuxlist.h"
#include "osmocom/core/msgb.h"
#include "llist_irqsafe.h" #include "llist_irqsafe.h"
#include "req_ctx.h" #include "usb_buf.h"
#include "cardemu_prot.h" #include "simtrace_prot.h"
#include "wwan_perst.h"
#include "sim_switch.h"
#define TRACE_ENTRY() TRACE_DEBUG("%s entering\r\n", __func__) #define TRACE_ENTRY() TRACE_DEBUG("%s entering\r\n", __func__)
#ifdef PINS_CARDSIM
static const Pin pins_cardsim[] = PINS_CARDSIM; static const Pin pins_cardsim[] = PINS_CARDSIM;
#endif
/* UART pins */ /* UART pins */
static const Pin pins_usim1[] = {PINS_USIM1}; static const Pin pins_usim1[] = {PINS_USIM1};
@@ -41,7 +46,7 @@ struct cardem_inst {
uint32_t vcc_uv_last; uint32_t vcc_uv_last;
}; };
static struct cardem_inst cardem_inst[] = { struct cardem_inst cardem_inst[] = {
{ {
.num = 0, .num = 0,
.usart_info = { .usart_info = {
@@ -52,7 +57,9 @@ static struct cardem_inst cardem_inst[] = {
.ep_out = PHONE_DATAOUT, .ep_out = PHONE_DATAOUT,
.ep_in = PHONE_DATAIN, .ep_in = PHONE_DATAIN,
.ep_int = PHONE_INT, .ep_int = PHONE_INT,
#ifdef PIN_SET_USIM1_PRES
.pin_insert = PIN_SET_USIM1_PRES, .pin_insert = PIN_SET_USIM1_PRES,
#endif
}, },
#ifdef CARDEMU_SECOND_UART #ifdef CARDEMU_SECOND_UART
{ {
@@ -65,7 +72,9 @@ static struct cardem_inst cardem_inst[] = {
.ep_out = CARDEM_USIM2_DATAOUT, .ep_out = CARDEM_USIM2_DATAOUT,
.ep_in = CARDEM_USIM2_DATAIN, .ep_in = CARDEM_USIM2_DATAIN,
.ep_int = CARDEM_USIM2_INT, .ep_int = CARDEM_USIM2_INT,
#ifdef PIN_SET_USIM2_PRES
.pin_insert = PIN_SET_USIM2_PRES, .pin_insert = PIN_SET_USIM2_PRES,
#endif
}, },
#endif #endif
}; };
@@ -371,7 +380,9 @@ void mode_cardemu_init(void)
TRACE_ENTRY(); TRACE_ENTRY();
#ifdef PINS_CARDSIM
PIO_Configure(pins_cardsim, PIO_LISTSIZE(pins_cardsim)); PIO_Configure(pins_cardsim, PIO_LISTSIZE(pins_cardsim));
#endif
#ifdef DETECT_VCC_BY_ADC #ifdef DETECT_VCC_BY_ADC
card_vcc_adc_init(); card_vcc_adc_init();
#endif /* DETECT_VCC_BY_ADC */ #endif /* DETECT_VCC_BY_ADC */
@@ -387,7 +398,7 @@ void mode_cardemu_init(void)
PIO_ConfigureIt(&pin_usim1_vcc, usim1_vcc_irqhandler); PIO_ConfigureIt(&pin_usim1_vcc, usim1_vcc_irqhandler);
PIO_EnableIt(&pin_usim1_vcc); PIO_EnableIt(&pin_usim1_vcc);
#endif /* DETECT_VCC_BY_ADC */ #endif /* DETECT_VCC_BY_ADC */
cardem_inst[0].ch = card_emu_init(0, 2, 0); cardem_inst[0].ch = card_emu_init(0, 2, 0, PHONE_DATAIN, PHONE_INT);
#ifdef CARDEMU_SECOND_UART #ifdef CARDEMU_SECOND_UART
INIT_LLIST_HEAD(&cardem_inst[1].usb_out_queue); INIT_LLIST_HEAD(&cardem_inst[1].usb_out_queue);
@@ -401,7 +412,7 @@ void mode_cardemu_init(void)
PIO_ConfigureIt(&pin_usim2_vcc, usim2_vcc_irqhandler); PIO_ConfigureIt(&pin_usim2_vcc, usim2_vcc_irqhandler);
PIO_EnableIt(&pin_usim2_vcc); PIO_EnableIt(&pin_usim2_vcc);
#endif /* DETECT_VCC_BY_ADC */ #endif /* DETECT_VCC_BY_ADC */
cardem_inst[1].ch = card_emu_init(1, 0, 1); cardem_inst[1].ch = card_emu_init(1, 0, 1, CARDEM_USIM2_DATAIN, CARDEM_USIM2_INT);
#endif /* CARDEMU_SECOND_UART */ #endif /* CARDEMU_SECOND_UART */
} }
@@ -411,7 +422,7 @@ void mode_cardemu_exit(void)
TRACE_ENTRY(); TRACE_ENTRY();
/* FIXME: stop tc_fdt */ /* FIXME: stop tc_fdt */
/* FIXME: release all rctx, unlink them from any queue */ /* FIXME: release all msg, unlink them from any queue */
PIO_DisableIt(&pin_usim1_rst); PIO_DisableIt(&pin_usim1_rst);
PIO_DisableIt(&pin_usim1_vcc); PIO_DisableIt(&pin_usim1_vcc);
@@ -431,84 +442,198 @@ void mode_cardemu_exit(void)
} }
/* handle a single USB command as received from the USB host */ /* handle a single USB command as received from the USB host */
static void dispatch_usb_command(struct req_ctx *rctx, struct cardem_inst *ci) static void dispatch_usb_command_generic(struct msgb *msg, struct cardem_inst *ci)
{ {
struct cardemu_usb_msg_hdr *hdr; struct simtrace_msg_hdr *hdr;
hdr = (struct simtrace_msg_hdr *) msg->l1h;
switch (hdr->msg_type) {
case SIMTRACE_CMD_BD_BOARD_INFO:
break;
default:
break;
}
usb_buf_free(msg);
}
/* handle a single USB command as received from the USB host */
static void dispatch_usb_command_cardem(struct msgb *msg, struct cardem_inst *ci)
{
struct simtrace_msg_hdr *hdr;
struct cardemu_usb_msg_set_atr *atr; struct cardemu_usb_msg_set_atr *atr;
struct cardemu_usb_msg_cardinsert *cardins; struct cardemu_usb_msg_cardinsert *cardins;
struct llist_head *queue; struct llist_head *queue;
hdr = (struct cardemu_usb_msg_hdr *) rctx->data; hdr = (struct simtrace_msg_hdr *) msg->l1h;
switch (hdr->msg_type) { switch (hdr->msg_type) {
case CEMU_USB_MSGT_DT_TX_DATA: case SIMTRACE_MSGT_DT_CEMU_TX_DATA:
queue = card_emu_get_uart_tx_queue(ci->ch); queue = card_emu_get_uart_tx_queue(ci->ch);
req_ctx_set_state(rctx, RCTX_S_UART_TX_PENDING); llist_add_tail(&msg->list, queue);
llist_add_tail(&rctx->list, queue);
card_emu_have_new_uart_tx(ci->ch); card_emu_have_new_uart_tx(ci->ch);
break; break;
case CEMU_USB_MSGT_DT_SET_ATR: case SIMTRACE_MSGT_DT_CEMU_SET_ATR:
atr = (struct cardemu_usb_msg_set_atr *) hdr; atr = (struct cardemu_usb_msg_set_atr *) msg->l2h;
card_emu_set_atr(ci->ch, atr->atr, atr->atr_len); card_emu_set_atr(ci->ch, atr->atr, atr->atr_len);
req_ctx_put(rctx); usb_buf_free(msg);
break; break;
case CEMU_USB_MSGT_DT_CARDINSERT: case SIMTRACE_MSGT_DT_CEMU_CARDINSERT:
cardins = (struct cardemu_usb_msg_cardinsert *) hdr; cardins = (struct cardemu_usb_msg_cardinsert *) msg->l2h;
TRACE_INFO("%u: set card_insert to %s\r\n", ci->num, TRACE_INFO("%u: set card_insert to %s\r\n", ci->num,
cardins->card_insert ? "INSERTED" : "REMOVED"); cardins->card_insert ? "INSERTED" : "REMOVED");
if (cardins->card_insert) if (cardins->card_insert)
PIO_Set(&ci->pin_insert); PIO_Set(&ci->pin_insert);
else else
PIO_Clear(&ci->pin_insert); PIO_Clear(&ci->pin_insert);
req_ctx_put(rctx); usb_buf_free(msg);
break; break;
case CEMU_USB_MSGT_DT_GET_STATUS: case SIMTRACE_MSGT_BD_CEMU_STATUS:
card_emu_report_status(ci->ch); card_emu_report_status(ci->ch);
usb_buf_free(msg);
break; break;
case CEMU_USB_MSGT_DT_GET_STATS: case SIMTRACE_MSGT_BD_CEMU_STATS:
default: default:
/* FIXME */ /* FIXME: Send Error */
req_ctx_put(rctx); usb_buf_free(msg);
break; break;
} }
} }
static void dispatch_received_rctx(struct req_ctx *rctx, struct cardem_inst *ci) static int usb_command_modem_reset(struct msgb *msg, struct cardem_inst *ci)
{ {
struct req_ctx *segm; struct st_modem_reset *mr = (struct st_modem_reset *) msg->l2h;
struct cardemu_usb_msg_hdr *mh;
int i = 0; if (msgb_l2len(msg) < sizeof(*mr))
return -1;
switch (mr->asserted) {
case 0:
wwan_perst_set(ci->num, 0);
break;
case 1:
wwan_perst_set(ci->num, 1);
break;
case 2:
wwan_perst_do_reset_pulse(ci->num, mr->pulse_duration_msec);
break;
default:
return -1;
}
return 0;
}
static int usb_command_sim_select(struct msgb *msg, struct cardem_inst *ci)
{
struct st_modem_sim_select *mss = (struct st_modem_sim_select *) msg->l2h;
if (msgb_l2len(msg) < sizeof(*mss))
return -1;
if (mss->remote_sim)
sim_switch_use_physical(ci->num, 0);
else
sim_switch_use_physical(ci->num, 1);
return 0;
}
/* handle a single USB command as received from the USB host */
static void dispatch_usb_command_modem(struct msgb *msg, struct cardem_inst *ci)
{
struct simtrace_msg_hdr *hdr;
hdr = (struct simtrace_msg_hdr *) msg->l1h;
switch (hdr->msg_type) {
case SIMTRACE_MSGT_DT_MODEM_RESET:
usb_command_modem_reset(msg, ci);
break;
case SIMTRACE_MSGT_DT_MODEM_SIM_SELECT:
usb_command_sim_select(msg, ci);
break;
case SIMTRACE_MSGT_BD_MODEM_STATUS:
break;
default:
break;
}
usb_buf_free(msg);
}
/* handle a single USB command as received from the USB host */
static void dispatch_usb_command(struct msgb *msg, struct cardem_inst *ci)
{
struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *) msg->l1h;
if (msgb_length(msg) < sizeof(*sh)) {
/* FIXME: Error */
usb_buf_free(msg);
return;
}
switch (sh->msg_class) {
case SIMTRACE_MSGC_GENERIC:
dispatch_usb_command_generic(msg, ci);
break;
case SIMTRACE_MSGC_CARDEM:
dispatch_usb_command_cardem(msg, ci);
break;
case SIMTRACE_MSGC_MODEM:
/* FIXME: Find out why this fails if used for !=
* MSGC_MODEM ?!? */
msg->l2h = msg->l1h + sizeof(*sh);
dispatch_usb_command_modem(msg, ci);
break;
default:
/* FIXME: Send Error */
usb_buf_free(msg);
break;
}
}
static void dispatch_received_msg(struct msgb *msg, struct cardem_inst *ci)
{
struct msgb *segm;
struct simtrace_msg_hdr *mh;
/* check if we have multiple concatenated commands in /* check if we have multiple concatenated commands in
* one message. USB endpoints are streams that don't * one message. USB endpoints are streams that don't
* preserve the message boundaries */ * preserve the message boundaries */
mh = (struct cardemu_usb_msg_hdr *) rctx->data; mh = (struct simtrace_msg_hdr *) msg->data;
if (mh->msg_len == rctx->tot_len) { if (mh->msg_len == msgb_length(msg)) {
/* fast path: only one message in buffer */ /* fast path: only one message in buffer */
dispatch_usb_command(rctx, ci); dispatch_usb_command(msg, ci);
return; return;
} }
/* slow path: iterate over list of messages, allocating one new /* slow path: iterate over list of messages, allocating one new
* reqe_ctx per segment */ * reqe_ctx per segment */
for (mh = (struct cardemu_usb_msg_hdr *) rctx->data; while (1) {
(uint8_t *)mh < rctx->data + rctx->tot_len; mh = (struct simtrace_msg_hdr *) msg->data;
mh = (struct cardemu_usb_msg_hdr * ) ((uint8_t *)mh + mh->msg_len)) {
segm = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_MAIN_PROCESSING); segm = usb_buf_alloc(ci->ep_out);
if (!segm) { if (!segm) {
TRACE_ERROR("%u: ENOMEM during rctx segmentation\r\n", TRACE_ERROR("%u: ENOMEM during msg segmentation\r\n",
ci->num); ci->num);
break; break;
} }
segm->idx = 0;
segm->tot_len = mh->msg_len; if (mh->msg_len > msgb_length(msg)) {
memcpy(segm->data, mh, segm->tot_len); TRACE_ERROR("%u: Unexpected large message (%u bytes)\n",
dispatch_usb_command(segm, ci); ci->num, mh->msg_len);
i++; usb_buf_free(segm);
} else {
uint8_t *cur = msgb_put(segm, mh->msg_len);
segm->l1h = segm->head;
memcpy(cur, mh, mh->msg_len);
dispatch_usb_command(segm, ci);
}
/* pull this message */
msgb_pull(msg, mh->msg_len);
/* abort if we're done */
if (msgb_length(msg) <= 0)
break;
} }
/* release the master req_ctx, as all segments have been usb_buf_free(msg);
* processed now */
req_ctx_put(rctx);
} }
/* iterate over the queue of incoming USB commands and dispatch/execute /* iterate over the queue of incoming USB commands and dispatch/execute
@@ -517,7 +642,7 @@ static void process_any_usb_commands(struct llist_head *main_q,
struct cardem_inst *ci) struct cardem_inst *ci)
{ {
struct llist_head *lh; struct llist_head *lh;
struct req_ctx *rctx; struct msgb *msg;
int i; int i;
/* limit the number of iterations to 10, to ensure we don't get /* limit the number of iterations to 10, to ensure we don't get
@@ -527,8 +652,8 @@ static void process_any_usb_commands(struct llist_head *main_q,
lh = llist_head_dequeue_irqsafe(main_q); lh = llist_head_dequeue_irqsafe(main_q);
if (!lh) if (!lh)
break; break;
rctx = llist_entry(lh, struct req_ctx, list); msg = llist_entry(lh, struct msgb, list);
dispatch_received_rctx(rctx, ci); dispatch_received_msg(msg, ci);
} }
} }
@@ -554,19 +679,16 @@ void mode_cardemu_run(void)
//TRACE_ERROR("%uRx%02x\r\n", i, byte); //TRACE_ERROR("%uRx%02x\r\n", i, byte);
} }
queue = card_emu_get_usb_tx_queue(ci->ch); /* first try to send any pending messages on IRQ */
int usb_pending = llist_count(queue); usb_refill_to_host(ci->ep_int);
if (usb_pending != ci->usb_pending_old) {
TRACE_DEBUG("%u usb_pending=%d\r\n", /* then try to send any pending messages on IN */
i, usb_pending); usb_refill_to_host(ci->ep_in);
ci->usb_pending_old = usb_pending;
}
usb_refill_to_host(queue, ci->ep_in);
/* ensure we can handle incoming USB messages from the /* ensure we can handle incoming USB messages from the
* host */ * host */
queue = &ci->usb_out_queue; usb_refill_from_host(ci->ep_out);
usb_refill_from_host(queue, ci->ep_out); queue = usb_get_queue(ci->ep_out);
process_any_usb_commands(queue, ci); process_any_usb_commands(queue, ci);
} }
} }

View File

@@ -0,0 +1,69 @@
#include <stdint.h>
#include "talloc.h"
#include "trace.h"
#include "osmocom/core/utils.h"
#define NUM_RCTX_SMALL 10
#define RCTX_SIZE_SMALL 348
static uint8_t msgb_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL] __attribute__((aligned(sizeof(long))));
static uint8_t msgb_inuse[NUM_RCTX_SMALL];
void *_talloc_zero(const void *ctx, size_t size, const char *name)
{
unsigned int i;
if (size > RCTX_SIZE_SMALL) {
TRACE_ERROR("%s() request too large(%d > %d)\r\n", __func__, size, RCTX_SIZE_SMALL);
return NULL;
}
for (i = 0; i < ARRAY_SIZE(msgb_inuse); i++) {
if (!msgb_inuse[i]) {
uint8_t *out = msgb_data[i];
msgb_inuse[i] = 1;
memset(out, 0, size);
return out;
}
}
TRACE_ERROR("%s() out of memory!\r\n", __func__);
return NULL;
}
int _talloc_free(void *ptr, const char *location)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(msgb_inuse); i++) {
if (ptr == msgb_data[i]) {
if (!msgb_inuse[i]) {
TRACE_ERROR("%s: double_free by \r\n", __func__, location);
} else {
msgb_inuse[i] = 0;
}
return 0;
}
}
TRACE_ERROR("%s: invalid pointer %p from %s\r\n", __func__, ptr, location);
return -1;
}
void talloc_set_name_const(const void *ptr, const char *name)
{
/* do nothing */
}
#if 0
void *talloc_named_const(const void *context, size_t size, const char *name)
{
if (size)
TRACE_ERROR("%s: called with size!=0 from %s\r\n", __func__, name);
return NULL;
}
void *talloc_pool(const void *context, size_t size)
{
}
#endif

View File

@@ -1,140 +0,0 @@
/* USB Request Context for OpenPCD / OpenPICC / SIMtrace
* (C) 2006-2016 by Harald Welte <hwelte@hmw-consulting.de>
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include "chip.h"
#include "utils.h"
#include "trace.h"
#include "req_ctx.h"
#define NUM_RCTX_SMALL 10
#define NUM_RCTX_LARGE 0
#define NUM_REQ_CTX (NUM_RCTX_SMALL+NUM_RCTX_LARGE)
static uint8_t rctx_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL];
static uint8_t rctx_data_large[NUM_RCTX_LARGE][RCTX_SIZE_LARGE];
static struct req_ctx req_ctx[NUM_REQ_CTX];
struct req_ctx __ramfunc *
req_ctx_find_get(int large, uint32_t old_state, uint32_t new_state)
{
struct req_ctx *toReturn = NULL;
unsigned long flags;
unsigned int i;
if (old_state >= RCTX_STATE_COUNT || new_state >= RCTX_STATE_COUNT) {
TRACE_DEBUG("Invalid parameters for req_ctx_find_get\r\n");
return NULL;
}
local_irq_save(flags);
for (i = 0; i < ARRAY_SIZE(req_ctx); i++) {
if (req_ctx[i].state == old_state) {
toReturn = &req_ctx[i];
toReturn->state = new_state;
break;
}
}
local_irq_restore(flags);
TRACE_DEBUG("%s(%u, %u, %u)=%p\r\n", __func__, large, old_state,
new_state, toReturn);
return toReturn;
}
uint8_t req_ctx_num(struct req_ctx *ctx)
{
return ctx - req_ctx;
}
void req_ctx_set_state(struct req_ctx *ctx, uint32_t new_state)
{
unsigned long flags;
TRACE_DEBUG("%s(ctx=%p, new_state=%u)\r\n", __func__, ctx, new_state);
if (new_state >= RCTX_STATE_COUNT) {
TRACE_DEBUG("Invalid new_state for req_ctx_set_state\r\n");
return;
}
local_irq_save(flags);
ctx->state = new_state;
local_irq_restore(flags);
}
#ifdef DEBUG_REQCTX
void req_print(int state) {
int count = 0;
struct req_ctx *ctx, *last = NULL;
DEBUGP("State [%02i] start <==> ", state);
ctx = req_ctx_queues[state];
while (ctx) {
if (last != ctx->prev)
DEBUGP("*INV_PREV* ");
DEBUGP("%08X => ", ctx);
last = ctx;
ctx = ctx->next;
count++;
if (count > NUM_REQ_CTX) {
DEBUGP("*WILD POINTER* => ");
break;
}
}
TRACE_DEBUG("NULL");
if (!req_ctx_queues[state] && req_ctx_tails[state]) {
TRACE_DEBUG("NULL head, NON-NULL tail\r\n");
}
if (last != req_ctx_tails[state]) {
TRACE_DEBUG("Tail does not match last element\r\n");
}
}
#endif
void req_ctx_put(struct req_ctx *ctx)
{
return req_ctx_set_state(ctx, RCTX_S_FREE);
}
void req_ctx_init(void)
{
int i;
for (i = 0; i < NUM_RCTX_SMALL; i++) {
req_ctx[i].size = RCTX_SIZE_SMALL;
req_ctx[i].tot_len = 0;
req_ctx[i].data = rctx_data[i];
req_ctx[i].state = RCTX_S_FREE;
TRACE_DEBUG("SMALL req_ctx[%02i] initialized at %p, Data: %p => %p\r\n",
i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_SMALL);
}
for (; i < NUM_REQ_CTX; i++) {
req_ctx[i].size = RCTX_SIZE_LARGE;
req_ctx[i].tot_len = 0;
req_ctx[i].data = rctx_data_large[i];
req_ctx[i].state = RCTX_S_FREE;
TRACE_DEBUG("LARGE req_ctx[%02i] initialized at %p, Data: %p => %p\r\n",
i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_LARGE);
}
}

View File

@@ -25,17 +25,17 @@
#include "chip.h" #include "chip.h"
/* pins for Channel 0 of TC-block 0 */ /* pins for Channel 0 of TC-block 0, we only use TCLK + TIOB */
#define PIN_TCLK0 {PIO_PA4, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT } #define PIN_TCLK0 {PIO_PA4, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT }
#define PIN_TIOA0 {PIO_PA0, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} #define PIN_TIOA0 {PIO_PA0, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
#define PIN_TIOB0 {PIO_PA1, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} #define PIN_TIOB0 {PIO_PA1, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
static const Pin pins_tc0[] = { PIN_TCLK0, PIN_TIOA0, PIN_TIOB0 }; static const Pin pins_tc0[] = { PIN_TCLK0, PIN_TIOB0 };
/* pins for Channel 2 of TC-block 0 */ /* pins for Channel 2 of TC-block 0, we only use TCLK + TIOB */
#define PIN_TCLK2 {PIO_PA29, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} #define PIN_TCLK2 {PIO_PA29, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
#define PIN_TIOA2 {PIO_PA26, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} #define PIN_TIOA2 {PIO_PA26, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
#define PIN_TIOB2 {PIO_PA27, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} #define PIN_TIOB2 {PIO_PA27, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
static const Pin pins_tc2[] = { PIN_TCLK2, PIN_TIOA2, PIN_TIOB2 }; static const Pin pins_tc2[] = { PIN_TCLK2, PIN_TIOB2 };
struct tc_etu_state { struct tc_etu_state {
/* total negotiated waiting time (default = 9600) */ /* total negotiated waiting time (default = 9600) */
@@ -158,7 +158,7 @@ void tc_etu_init(uint8_t chan_nr, void *handle)
switch (chan_nr) { switch (chan_nr) {
case 0: case 0:
/* Configure PA4(TCLK0), PA0(TIOA0), PA1(TIB0) */ /* Configure PA4(TCLK0), PA1(TIB0) */
PIO_Configure(pins_tc0, ARRAY_SIZE(pins_tc0)); PIO_Configure(pins_tc0, ARRAY_SIZE(pins_tc0));
PMC_EnablePeripheral(ID_TC0); PMC_EnablePeripheral(ID_TC0);
/* route TCLK0 to XC2 */ /* route TCLK0 to XC2 */
@@ -171,7 +171,7 @@ void tc_etu_init(uint8_t chan_nr, void *handle)
te->chan = &TC0->TC_CHANNEL[0]; te->chan = &TC0->TC_CHANNEL[0];
break; break;
case 2: case 2:
/* Configure PA29(TCLK2), PA26(TIOA2), PA27(TIOB2) */ /* Configure PA29(TCLK2), PA27(TIOB2) */
PIO_Configure(pins_tc2, ARRAY_SIZE(pins_tc2)); PIO_Configure(pins_tc2, ARRAY_SIZE(pins_tc2));
PMC_EnablePeripheral(ID_TC2); PMC_EnablePeripheral(ID_TC2);
/* route TCLK2 to XC2. TC0 really means TCA in this case */ /* route TCLK2 to XC2. TC0 really means TCA in this case */

View File

@@ -39,191 +39,13 @@
#include <usb/common/dfu/usb_dfu.h> #include <usb/common/dfu/usb_dfu.h>
#include <usb/device/dfu/dfu.h> #include <usb/device/dfu/dfu.h>
#define SIMTRACE_SUBCLASS_SNIFFER 1
#define SIMTRACE_SUBCLASS_CARDEM 2
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* USB String descriptors * USB String descriptors
*------------------------------------------------------------------------------*/ *------------------------------------------------------------------------------*/
#include "usb_strings_generated.h"
static const unsigned char langDesc[] = {
USBStringDescriptor_LENGTH(1),
USBGenericDescriptor_STRING,
USBStringDescriptor_ENGLISH_US
};
const unsigned char manufStringDescriptor[] = {
USBStringDescriptor_LENGTH(24),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('s'),
USBStringDescriptor_UNICODE('y'),
USBStringDescriptor_UNICODE('s'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('o'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('o'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('-'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('s'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE('f'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('.'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('G'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('b'),
USBStringDescriptor_UNICODE('H'),
};
const unsigned char productStringDescriptor[] = {
USBStringDescriptor_LENGTH(10),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('e'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('2'),
};
const unsigned char snifferConfigStringDescriptor[] = {
USBStringDescriptor_LENGTH(16),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('e'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('n'),
USBStringDescriptor_UNICODE('i'),
USBStringDescriptor_UNICODE('f'),
USBStringDescriptor_UNICODE('f'),
USBStringDescriptor_UNICODE('e'),
USBStringDescriptor_UNICODE('r'),
};
const unsigned char CCIDConfigStringDescriptor[] = {
USBStringDescriptor_LENGTH(13),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('e'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('C'),
USBStringDescriptor_UNICODE('C'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('D'),
};
const unsigned char phoneConfigStringDescriptor[] = {
USBStringDescriptor_LENGTH(14),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('e'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('P'),
USBStringDescriptor_UNICODE('h'),
USBStringDescriptor_UNICODE('o'),
USBStringDescriptor_UNICODE('n'),
USBStringDescriptor_UNICODE('e'),
};
const unsigned char MITMConfigStringDescriptor[] = {
USBStringDescriptor_LENGTH(13),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('c'),
USBStringDescriptor_UNICODE('e'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('T'),
USBStringDescriptor_UNICODE('M'),
};
const unsigned char cardem_usim1_intf_str[] = {
USBStringDescriptor_LENGTH(18),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('C'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('d'),
USBStringDescriptor_UNICODE('E'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('u'),
USBStringDescriptor_UNICODE('l'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('o'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('U'),
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('1'),
};
const unsigned char cardem_usim2_intf_str[] = {
USBStringDescriptor_LENGTH(18),
USBGenericDescriptor_STRING,
USBStringDescriptor_UNICODE('C'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE('d'),
USBStringDescriptor_UNICODE('E'),
USBStringDescriptor_UNICODE('m'),
USBStringDescriptor_UNICODE('u'),
USBStringDescriptor_UNICODE('l'),
USBStringDescriptor_UNICODE('a'),
USBStringDescriptor_UNICODE('t'),
USBStringDescriptor_UNICODE('o'),
USBStringDescriptor_UNICODE('r'),
USBStringDescriptor_UNICODE(' '),
USBStringDescriptor_UNICODE('U'),
USBStringDescriptor_UNICODE('S'),
USBStringDescriptor_UNICODE('I'),
USBStringDescriptor_UNICODE('M'),
USBStringDescriptor_UNICODE('2'),
};
enum strDescNum { enum strDescNum {
PRODUCT_STRING = 1, PRODUCT_STRING = 1,
MANUF_STR, MANUF_STR,
@@ -236,19 +58,6 @@ enum strDescNum {
STRING_DESC_CNT STRING_DESC_CNT
}; };
/** List of string descriptors used by the device */
static const unsigned char *stringDescriptors[] = {
langDesc,
[PRODUCT_STRING] = productStringDescriptor,
[MANUF_STR] = manufStringDescriptor,
[SNIFFER_CONF_STR] = snifferConfigStringDescriptor,
[CCID_CONF_STR] = CCIDConfigStringDescriptor,
[PHONE_CONF_STR] = phoneConfigStringDescriptor,
[MITM_CONF_STR] = MITMConfigStringDescriptor,
[CARDEM_USIM1_INTF_STR] = cardem_usim1_intf_str,
[CARDEM_USIM2_INTF_STR] = cardem_usim2_intf_str,
};
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* USB Device descriptors * USB Device descriptors
*------------------------------------------------------------------------------*/ *------------------------------------------------------------------------------*/
@@ -268,7 +77,7 @@ typedef struct _SIMTraceDriverConfigurationDescriptorSniffer {
static const SIMTraceDriverConfigurationDescriptorSniffer static const SIMTraceDriverConfigurationDescriptorSniffer
configurationDescriptorSniffer = { configurationDescriptorSniffer = {
/* Standard configuration descriptor */ /* Standard configuration descriptor */
{ .configuration = {
.bLength = sizeof(USBConfigurationDescriptor), .bLength = sizeof(USBConfigurationDescriptor),
.bDescriptorType = USBGenericDescriptor_CONFIGURATION, .bDescriptorType = USBGenericDescriptor_CONFIGURATION,
.wTotalLength = sizeof(SIMTraceDriverConfigurationDescriptorSniffer), .wTotalLength = sizeof(SIMTraceDriverConfigurationDescriptorSniffer),
@@ -279,19 +88,19 @@ static const SIMTraceDriverConfigurationDescriptorSniffer
.bMaxPower = USBConfigurationDescriptor_POWER(100), .bMaxPower = USBConfigurationDescriptor_POWER(100),
}, },
/* Communication class interface standard descriptor */ /* Communication class interface standard descriptor */
{ .sniffer = {
.bLength = sizeof(USBInterfaceDescriptor), .bLength = sizeof(USBInterfaceDescriptor),
.bDescriptorType = USBGenericDescriptor_INTERFACE, .bDescriptorType = USBGenericDescriptor_INTERFACE,
.bInterfaceNumber = 0, .bInterfaceNumber = 0,
.bAlternateSetting = 0, .bAlternateSetting = 0,
.bNumEndpoints = 3, .bNumEndpoints = 3,
.bInterfaceClass = 0xff, .bInterfaceClass = 0xff,
.bInterfaceSubClass = 0, .bInterfaceSubClass = SIMTRACE_SUBCLASS_SNIFFER,
.bInterfaceProtocol = 0, .bInterfaceProtocol = 0,
.iInterface = SNIFFER_CONF_STR, .iInterface = SNIFFER_CONF_STR,
}, },
/* Bulk-OUT endpoint standard descriptor */ /* Bulk-OUT endpoint standard descriptor */
{ .sniffer_dataOut = {
.bLength = sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
.bDescriptorType = USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
.bEndpointAddress = USBEndpointDescriptor_ADDRESS( .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
@@ -304,7 +113,7 @@ static const SIMTraceDriverConfigurationDescriptorSniffer
.bInterval = 0, .bInterval = 0,
}, },
/* Bulk-IN endpoint descriptor */ /* Bulk-IN endpoint descriptor */
{ .sniffer_dataIn = {
.bLength = sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
.bDescriptorType = USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
.bEndpointAddress = USBEndpointDescriptor_ADDRESS( .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
@@ -317,7 +126,7 @@ static const SIMTraceDriverConfigurationDescriptorSniffer
.bInterval = 0, .bInterval = 0,
}, },
// Notification endpoint descriptor // Notification endpoint descriptor
{ .sniffer_interruptIn = {
.bLength = sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
.bDescriptorType = USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
.bEndpointAddress = USBEndpointDescriptor_ADDRESS( .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
@@ -452,111 +261,117 @@ typedef struct _SIMTraceDriverConfigurationDescriptorPhone {
static const SIMTraceDriverConfigurationDescriptorPhone static const SIMTraceDriverConfigurationDescriptorPhone
configurationDescriptorPhone = { configurationDescriptorPhone = {
/* Standard configuration descriptor */ /* Standard configuration descriptor */
{ .configuration = {
sizeof(USBConfigurationDescriptor), .bLength = sizeof(USBConfigurationDescriptor),
USBGenericDescriptor_CONFIGURATION, .bDescriptorType = USBGenericDescriptor_CONFIGURATION,
sizeof(SIMTraceDriverConfigurationDescriptorPhone), .wTotalLength = sizeof(SIMTraceDriverConfigurationDescriptorPhone),
#ifdef CARDEMU_SECOND_UART #ifdef CARDEMU_SECOND_UART
2+DFURT_NUM_IF, .bNumInterfaces = 2+DFURT_NUM_IF,
#else #else
1+DFURT_NUM_IF, /* There is one interface in this configuration */ .bNumInterefaces = 1+DFURT_NUM_IF,
#endif #endif
CFG_NUM_PHONE, /* configuration number */ .bConfigurationValue = CFG_NUM_PHONE,
PHONE_CONF_STR, /* string descriptor for this configuration */ .iConfiguration = PHONE_CONF_STR,
USBD_BMATTRIBUTES, .bmAttributes = USBD_BMATTRIBUTES,
USBConfigurationDescriptor_POWER(100) .bMaxPower = USBConfigurationDescriptor_POWER(100)
}, },
/* Communication class interface standard descriptor */ /* Communication class interface standard descriptor */
{ .phone = {
sizeof(USBInterfaceDescriptor), .bLength = sizeof(USBInterfaceDescriptor),
USBGenericDescriptor_INTERFACE, .bDescriptorType = USBGenericDescriptor_INTERFACE,
0, /* This is interface #0 */ .bInterfaceNumber = 0,
0, /* This is alternate setting #0 for this interface */ .bAlternateSetting = 0,
3, /* Number of endpoints */ .bNumEndpoints = 3,
0xff, /* Descriptor Class: Vendor specific */ .bInterfaceClass = 0xff,
0, /* No subclass */ .bInterfaceSubClass = SIMTRACE_SUBCLASS_CARDEM,
0, /* No l */ .bInterfaceProtocol = 0,
CARDEM_USIM1_INTF_STR .iInterface = CARDEM_USIM1_INTF_STR,
}, },
/* Bulk-OUT endpoint standard descriptor */ /* Bulk-OUT endpoint standard descriptor */
{ .phone_dataOut = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
PHONE_DATAOUT), USBEndpointDescriptor_OUT,
USBEndpointDescriptor_BULK, PHONE_DATAOUT),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAOUT), .bmAttributes = USBEndpointDescriptor_BULK,
USBEndpointDescriptor_MAXBULKSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAOUT),
0 /* Must be 0 for full-speed bulk endpoints */ USBEndpointDescriptor_MAXBULKSIZE_FS),
.bInterval = 0 /* Must be 0 for full-speed bulk endpoints */
}, },
/* Bulk-IN endpoint descriptor */ /* Bulk-IN endpoint descriptor */
{ .phone_dataIn = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
PHONE_DATAIN), USBEndpointDescriptor_IN,
USBEndpointDescriptor_BULK, PHONE_DATAIN),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAIN), .bmAttributes = USBEndpointDescriptor_BULK,
USBEndpointDescriptor_MAXBULKSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAIN),
0 /* Must be 0 for full-speed bulk endpoints */ USBEndpointDescriptor_MAXBULKSIZE_FS),
.bInterval = 0 /* Must be 0 for full-speed bulk endpoints */
}, },
/* Notification endpoint descriptor */ /* Notification endpoint descriptor */
{ .phone_interruptIn = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
PHONE_INT), USBEndpointDescriptor_IN,
USBEndpointDescriptor_INTERRUPT, PHONE_INT),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_INT), .bmAttributes = USBEndpointDescriptor_INTERRUPT,
USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_INT),
0x10 USBEndpointDescriptor_MAXINTERRUPTSIZE_FS),
.bInterval = 0x10
}, },
#ifdef CARDEMU_SECOND_UART #ifdef CARDEMU_SECOND_UART
/* Communication class interface standard descriptor */ /* Communication class interface standard descriptor */
{ .usim2 = {
sizeof(USBInterfaceDescriptor), .bLength = sizeof(USBInterfaceDescriptor),
USBGenericDescriptor_INTERFACE, .bDescriptorType = USBGenericDescriptor_INTERFACE,
1, /* This is interface #1 */ .bInterfaceNumber = 1,
0, /* This is alternate setting #0 for this interface */ .bAlternateSetting = 0,
3, /* Number of endpoints */ .bNumEndpoints = 3,
0xff, /* Descriptor Class: Vendor specific */ .bInterfaceClass = 0xff,
0, /* No subclass */ .bInterfaceSubClass = SIMTRACE_SUBCLASS_CARDEM,
0, /* No l */ .bInterfaceProtocol = 0,
CARDEM_USIM2_INTF_STR .iInterface = CARDEM_USIM2_INTF_STR,
}, },
/* Bulk-OUT endpoint standard descriptor */ /* Bulk-OUT endpoint standard descriptor */
{ .usim2_dataOut = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
CARDEM_USIM2_DATAOUT), USBEndpointDescriptor_OUT,
USBEndpointDescriptor_BULK, CARDEM_USIM2_DATAOUT),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CARDEM_USIM2_DATAOUT), .bmAttributes = USBEndpointDescriptor_BULK,
USBEndpointDescriptor_MAXBULKSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CARDEM_USIM2_DATAOUT),
0 /* Must be 0 for full-speed bulk endpoints */ USBEndpointDescriptor_MAXBULKSIZE_FS),
.bInterval = 0 /* Must be 0 for full-speed bulk endpoints */
} }
, ,
/* Bulk-IN endpoint descriptor */ /* Bulk-IN endpoint descriptor */
{ .usim2_dataIn = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
CARDEM_USIM2_DATAIN), USBEndpointDescriptor_IN,
USBEndpointDescriptor_BULK, CARDEM_USIM2_DATAIN),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CARDEM_USIM2_DATAIN), .bmAttributes = USBEndpointDescriptor_BULK,
USBEndpointDescriptor_MAXBULKSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CARDEM_USIM2_DATAIN),
0 /* Must be 0 for full-speed bulk endpoints */ USBEndpointDescriptor_MAXBULKSIZE_FS),
.bInterval = 0 /* Must be 0 for full-speed bulk endpoints */
}, },
/* Notification endpoint descriptor */ /* Notification endpoint descriptor */
{ .usim2_interruptIn = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
CARDEM_USIM2_INT), USBEndpointDescriptor_IN,
USBEndpointDescriptor_INTERRUPT, CARDEM_USIM2_INT),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CARDEM_USIM2_INT), .bmAttributes = USBEndpointDescriptor_INTERRUPT,
USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CARDEM_USIM2_INT),
0x10 USBEndpointDescriptor_MAXINTERRUPTSIZE_FS),
.bInterval = 0x10,
}, },
DFURT_IF_DESCRIPTOR(2, 0), DFURT_IF_DESCRIPTOR(2, 0),
#else #else
@@ -591,20 +406,20 @@ typedef struct _SIMTraceDriverConfigurationDescriptorMITM {
static const SIMTraceDriverConfigurationDescriptorMITM static const SIMTraceDriverConfigurationDescriptorMITM
configurationDescriptorMITM = { configurationDescriptorMITM = {
/* Standard configuration descriptor */ /* Standard configuration descriptor */
{ .configuration = {
sizeof(USBConfigurationDescriptor), .bLength = sizeof(USBConfigurationDescriptor),
USBGenericDescriptor_CONFIGURATION, .bDescriptorType = USBGenericDescriptor_CONFIGURATION,
sizeof(SIMTraceDriverConfigurationDescriptorMITM), .wTotalLength = sizeof(SIMTraceDriverConfigurationDescriptorMITM),
2+DFURT_NUM_IF, /* There are two interfaces in this configuration */ .bNumInterfaces = 2+DFURT_NUM_IF,
CFG_NUM_MITM, /* configuration number */ .bConfigurationValue = CFG_NUM_MITM,
MITM_CONF_STR, /* string descriptor for this configuration */ .iConfiguration = MITM_CONF_STR,
USBD_BMATTRIBUTES, .bmAttributes = USBD_BMATTRIBUTES,
USBConfigurationDescriptor_POWER(100) .bMaxPower = USBConfigurationDescriptor_POWER(100),
}, },
// CCID interface descriptor // CCID interface descriptor
// Table 4.3-1 Interface Descriptor // Table 4.3-1 Interface Descriptor
// Interface descriptor // Interface descriptor
{ .simcard = {
.bLength = sizeof(USBInterfaceDescriptor), .bLength = sizeof(USBInterfaceDescriptor),
.bDescriptorType = USBGenericDescriptor_INTERFACE, .bDescriptorType = USBGenericDescriptor_INTERFACE,
.bInterfaceNumber = 0, .bInterfaceNumber = 0,
@@ -615,7 +430,7 @@ static const SIMTraceDriverConfigurationDescriptorMITM
.bInterfaceProtocol = 0, .bInterfaceProtocol = 0,
.iInterface = CCID_CONF_STR, .iInterface = CCID_CONF_STR,
}, },
{ .ccid = {
.bLength = sizeof(CCIDDescriptor), .bLength = sizeof(CCIDDescriptor),
.bDescriptorType = CCID_DECRIPTOR_TYPE, .bDescriptorType = CCID_DECRIPTOR_TYPE,
.bcdCCID = CCID1_10, // CCID version .bcdCCID = CCID1_10, // CCID version
@@ -644,7 +459,7 @@ static const SIMTraceDriverConfigurationDescriptorMITM
.bMaxCCIDBusySlots = 1, .bMaxCCIDBusySlots = 1,
}, },
// Bulk-OUT endpoint descriptor // Bulk-OUT endpoint descriptor
{ .simcard_dataOut = {
.bLength = sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
.bDescriptorType = USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
.bEndpointAddress = .bEndpointAddress =
@@ -657,7 +472,7 @@ static const SIMTraceDriverConfigurationDescriptorMITM
.bInterval = 0x00, .bInterval = 0x00,
}, },
// Bulk-IN endpoint descriptor // Bulk-IN endpoint descriptor
{ .simcard_dataIn = {
.bLength = sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
.bDescriptorType = USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
.bEndpointAddress = .bEndpointAddress =
@@ -670,7 +485,7 @@ static const SIMTraceDriverConfigurationDescriptorMITM
.bInterval = 0x00, .bInterval = 0x00,
}, },
// Notification endpoint descriptor // Notification endpoint descriptor
{ .simcard_interruptIn = {
.bLength = sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
.bDescriptorType = USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
.bEndpointAddress = .bEndpointAddress =
@@ -684,51 +499,53 @@ static const SIMTraceDriverConfigurationDescriptorMITM
}, },
/* Communication class interface standard descriptor */ /* Communication class interface standard descriptor */
{ .phone = {
sizeof(USBInterfaceDescriptor), .bLength = sizeof(USBInterfaceDescriptor),
USBGenericDescriptor_INTERFACE, .bDescriptorType = USBGenericDescriptor_INTERFACE,
1, /* This is interface #1 */ .bInterfaceNumber = 1,
0, /* This is alternate setting #0 for this interface */ .bAlternateSetting = 0,
3, /* Number of endpoints */ .bNumEndpoints = 3,
0xff, .bInterfaceClass = 0xff,
0, .bInterfaceSubClass = SIMTRAC_SUBCLASS_CARDEM,
0, .bInterfaceProtocol = 0,
PHONE_CONF_STR, /* string descriptor for this interface */ .iInterface = PHONE_CONF_STR,
} },
,
/* Bulk-OUT endpoint standard descriptor */ /* Bulk-OUT endpoint standard descriptor */
{ .phone_dataOut = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_OUT, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
PHONE_DATAOUT), USBEndpointDescriptor_OUT,
USBEndpointDescriptor_BULK, PHONE_DATAOUT),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAOUT), .bmAttributes = USBEndpointDescriptor_BULK,
USBEndpointDescriptor_MAXBULKSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAOUT),
0 /* Must be 0 for full-speed bulk endpoints */ USBEndpointDescriptor_MAXBULKSIZE_FS),
} .bInterval = 0, /* Must be 0 for full-speed bulk endpoints */
, },
/* Bulk-IN endpoint descriptor */ /* Bulk-IN endpoint descriptor */
{ .phone_dataIn = {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
PHONE_DATAIN), USBEndpointDescriptor_IN,
USBEndpointDescriptor_BULK, PHONE_DATAIN),
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAIN), .bmAttributes = USBEndpointDescriptor_BULK,
USBEndpointDescriptor_MAXBULKSIZE_FS), .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_DATAIN),
0 /* Must be 0 for full-speed bulk endpoints */ USBEndpointDescriptor_MAXBULKSIZE_FS),
} .bInterval = 0, /* Must be 0 for full-speed bulk endpoints */
, },
/* Notification endpoint descriptor */ /* Notification endpoint descriptor */
{ {
sizeof(USBEndpointDescriptor), .bLength = sizeof(USBEndpointDescriptor),
USBGenericDescriptor_ENDPOINT, .bDescriptorType = USBGenericDescriptor_ENDPOINT,
USBEndpointDescriptor_ADDRESS(USBEndpointDescriptor_IN, PHONE_INT), .bEndpointAddress = USBEndpointDescriptor_ADDRESS(
USBEndpointDescriptor_INTERRUPT, USBEndpointDescriptor_IN,
MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_INT), PHONE_INT),
USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), .bmAttributes = USBEndpointDescriptor_INTERRUPT,
0x10}, .wMaxPacketSize = MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(PHONE_INT),
USBEndpointDescriptor_MAXINTERRUPTSIZE_FS),
.bInterval = 0x10
},
DFURT_IF_DESCRIPTOR(2, 0), DFURT_IF_DESCRIPTOR(2, 0),
}; };
#endif /* HAVE_CARDEM */ #endif /* HAVE_CARDEM */
@@ -753,12 +570,12 @@ const USBDeviceDescriptor deviceDescriptor = {
.bLength = sizeof(USBDeviceDescriptor), .bLength = sizeof(USBDeviceDescriptor),
.bDescriptorType = USBGenericDescriptor_DEVICE, .bDescriptorType = USBGenericDescriptor_DEVICE,
.bcdUSB = USBDeviceDescriptor_USB2_00, .bcdUSB = USBDeviceDescriptor_USB2_00,
.bDeviceClass = 0xff, .bDeviceClass = 0,
.bDeviceSubClass = 0xff, .bDeviceSubClass = 0,
.bDeviceProtocol = 0xff, .bDeviceProtocol = 0,
.bMaxPacketSize0 = BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), .bMaxPacketSize0 = BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0),
.idVendor = SIMTRACE_VENDOR_ID, .idVendor = BOARD_USB_VENDOR_ID,
.idProduct = SIMTRACE_PRODUCT_ID, .idProduct = BOARD_USB_PRODUCT_ID,
.bcdDevice = 2, /* Release number */ .bcdDevice = 2, /* Release number */
.iManufacturer = MANUF_STR, .iManufacturer = MANUF_STR,
.iProduct = PRODUCT_STRING, .iProduct = PRODUCT_STRING,
@@ -776,8 +593,8 @@ static const USBDDriverDescriptors driverDescriptors = {
0, /* No high-speed configuration descriptor */ 0, /* No high-speed configuration descriptor */
0, /* No high-speed device qualifier descriptor */ 0, /* No high-speed device qualifier descriptor */
0, /* No high-speed other speed configuration descriptor */ 0, /* No high-speed other speed configuration descriptor */
stringDescriptors, usb_strings,
STRING_DESC_CNT /* cnt string descriptors in list */ ARRAY_SIZE(usb_strings),/* cnt string descriptors in list */
}; };
/*---------------------------------------------------------------------------- /*----------------------------------------------------------------------------

View File

@@ -0,0 +1,76 @@
#include "board.h"
#include "trace.h"
#include "usb_buf.h"
#include "osmocom/core/linuxlist.h"
#include "osmocom/core/msgb.h"
#include <errno.h>
#define USB_ALLOC_SIZE 280
static struct usb_buffered_ep usb_buffered_ep[BOARD_USB_NUMENDPOINTS];
struct usb_buffered_ep *usb_get_buf_ep(uint8_t ep)
{
if (ep >= ARRAY_SIZE(usb_buffered_ep))
return NULL;
return &usb_buffered_ep[ep];
}
/***********************************************************************
* User API
***********************************************************************/
struct llist_head *usb_get_queue(uint8_t ep)
{
struct usb_buffered_ep *bep = usb_get_buf_ep(ep);
if (!bep)
return NULL;
return &bep->queue;
}
/* allocate a USB buffer for use with given end-point */
struct msgb *usb_buf_alloc(uint8_t ep)
{
struct msgb *msg;
msg = msgb_alloc(USB_ALLOC_SIZE, "USB");
if (!msg)
return NULL;
msg->dst = usb_get_buf_ep(ep);
return msg;
}
/* release/return the USB buffer to the pool */
void usb_buf_free(struct msgb *msg)
{
msgb_free(msg);
}
/* submit a USB buffer for transmission to host */
int usb_buf_submit(struct msgb *msg)
{
struct usb_buffered_ep *ep = msg->dst;
if (!msg->dst) {
TRACE_ERROR("%s: msg without dst\r\n", __func__);
usb_buf_free(msg);
return -EINVAL;
}
/* no need for irqsafe operation, as the usb_tx_queue is
* processed only by the main loop context */
msgb_enqueue(&ep->queue, msg);
return 0;
}
void usb_buf_init(void)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(usb_buffered_ep); i++) {
struct usb_buffered_ep *ep = &usb_buffered_ep[i];
INIT_LLIST_HEAD(&ep->queue);
}
}

View File

@@ -0,0 +1,4 @@
#pragma once
void osmo_generate_backtrace(void);
void osmo_log_backtrace(int subsys, int level);

View File

@@ -0,0 +1,105 @@
/*
* bit16gen.h
*
* Copyright (C) 2014 Max <max.suraev@fairwaves.co>
*
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
/*! \brief load unaligned n-byte integer (little-endian encoding) into uint16_t
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns 16 bit unsigned integer
*/
static inline uint16_t osmo_load16le_ext(const void *p, uint8_t n)
{
uint8_t i;
uint16_t r = 0;
const uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; r |= ((uint16_t)q[i] << (8 * i)), i++);
return r;
}
/*! \brief load unaligned n-byte integer (big-endian encoding) into uint16_t
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns 16 bit unsigned integer
*/
static inline uint16_t osmo_load16be_ext(const void *p, uint8_t n)
{
uint8_t i;
uint16_t r = 0;
const uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; r |= ((uint16_t)q[i] << (16 - 8* (1 + i))), i++);
return r;
}
/*! \brief store unaligned n-byte integer (little-endian encoding) from uint16_t
* \param[in] x unsigned 16 bit integer
* \param[out] p Buffer to store integer
* \param[in] n Number of bytes to store
*/
static inline void osmo_store16le_ext(uint16_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
}
/*! \brief store unaligned n-byte integer (big-endian encoding) from uint16_t
* \param[in] x unsigned 16 bit integer
* \param[out] p Buffer to store integer
* \param[in] n Number of bytes to store
*/
static inline void osmo_store16be_ext(uint16_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
}
/* Convenience function for most-used cases */
/*! \brief load unaligned 16-bit integer (little-endian encoding) */
static inline uint16_t osmo_load16le(const void *p)
{
return osmo_load16le_ext(p, 16 / 8);
}
/*! \brief load unaligned 16-bit integer (big-endian encoding) */
static inline uint16_t osmo_load16be(const void *p)
{
return osmo_load16be_ext(p, 16 / 8);
}
/*! \brief store unaligned 16-bit integer (little-endian encoding) */
static inline void osmo_store16le(uint16_t x, void *p)
{
osmo_store16le_ext(x, p, 16 / 8);
}
/*! \brief store unaligned 16-bit integer (big-endian encoding) */
static inline void osmo_store16be(uint16_t x, void *p)
{
osmo_store16be_ext(x, p, 16 / 8);
}

View File

@@ -0,0 +1,105 @@
/*
* bit32gen.h
*
* Copyright (C) 2014 Max <max.suraev@fairwaves.co>
*
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
/*! \brief load unaligned n-byte integer (little-endian encoding) into uint32_t
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns 32 bit unsigned integer
*/
static inline uint32_t osmo_load32le_ext(const void *p, uint8_t n)
{
uint8_t i;
uint32_t r = 0;
const uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; r |= ((uint32_t)q[i] << (8 * i)), i++);
return r;
}
/*! \brief load unaligned n-byte integer (big-endian encoding) into uint32_t
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns 32 bit unsigned integer
*/
static inline uint32_t osmo_load32be_ext(const void *p, uint8_t n)
{
uint8_t i;
uint32_t r = 0;
const uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; r |= ((uint32_t)q[i] << (32 - 8* (1 + i))), i++);
return r;
}
/*! \brief store unaligned n-byte integer (little-endian encoding) from uint32_t
* \param[in] x unsigned 32 bit integer
* \param[out] p Buffer to store integer
* \param[in] n Number of bytes to store
*/
static inline void osmo_store32le_ext(uint32_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
}
/*! \brief store unaligned n-byte integer (big-endian encoding) from uint32_t
* \param[in] x unsigned 32 bit integer
* \param[out] p Buffer to store integer
* \param[in] n Number of bytes to store
*/
static inline void osmo_store32be_ext(uint32_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
}
/* Convenience function for most-used cases */
/*! \brief load unaligned 32-bit integer (little-endian encoding) */
static inline uint32_t osmo_load32le(const void *p)
{
return osmo_load32le_ext(p, 32 / 8);
}
/*! \brief load unaligned 32-bit integer (big-endian encoding) */
static inline uint32_t osmo_load32be(const void *p)
{
return osmo_load32be_ext(p, 32 / 8);
}
/*! \brief store unaligned 32-bit integer (little-endian encoding) */
static inline void osmo_store32le(uint32_t x, void *p)
{
osmo_store32le_ext(x, p, 32 / 8);
}
/*! \brief store unaligned 32-bit integer (big-endian encoding) */
static inline void osmo_store32be(uint32_t x, void *p)
{
osmo_store32be_ext(x, p, 32 / 8);
}

View File

@@ -0,0 +1,105 @@
/*
* bit64gen.h
*
* Copyright (C) 2014 Max <max.suraev@fairwaves.co>
*
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
/*! \brief load unaligned n-byte integer (little-endian encoding) into uint64_t
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns 64 bit unsigned integer
*/
static inline uint64_t osmo_load64le_ext(const void *p, uint8_t n)
{
uint8_t i;
uint64_t r = 0;
const uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; r |= ((uint64_t)q[i] << (8 * i)), i++);
return r;
}
/*! \brief load unaligned n-byte integer (big-endian encoding) into uint64_t
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns 64 bit unsigned integer
*/
static inline uint64_t osmo_load64be_ext(const void *p, uint8_t n)
{
uint8_t i;
uint64_t r = 0;
const uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; r |= ((uint64_t)q[i] << (64 - 8* (1 + i))), i++);
return r;
}
/*! \brief store unaligned n-byte integer (little-endian encoding) from uint64_t
* \param[in] x unsigned 64 bit integer
* \param[out] p Buffer to store integer
* \param[in] n Number of bytes to store
*/
static inline void osmo_store64le_ext(uint64_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
}
/*! \brief store unaligned n-byte integer (big-endian encoding) from uint64_t
* \param[in] x unsigned 64 bit integer
* \param[out] p Buffer to store integer
* \param[in] n Number of bytes to store
*/
static inline void osmo_store64be_ext(uint64_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
}
/* Convenience function for most-used cases */
/*! \brief load unaligned 64-bit integer (little-endian encoding) */
static inline uint64_t osmo_load64le(const void *p)
{
return osmo_load64le_ext(p, 64 / 8);
}
/*! \brief load unaligned 64-bit integer (big-endian encoding) */
static inline uint64_t osmo_load64be(const void *p)
{
return osmo_load64be_ext(p, 64 / 8);
}
/*! \brief store unaligned 64-bit integer (little-endian encoding) */
static inline void osmo_store64le(uint64_t x, void *p)
{
osmo_store64le_ext(x, p, 64 / 8);
}
/*! \brief store unaligned 64-bit integer (big-endian encoding) */
static inline void osmo_store64be(uint64_t x, void *p)
{
osmo_store64be_ext(x, p, 64 / 8);
}

View File

@@ -0,0 +1,118 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <osmocom/core/bit16gen.h>
#include <osmocom/core/bit32gen.h>
#include <osmocom/core/bit64gen.h>
/*! \defgroup bits soft, unpacked and packed bits
* @{
*/
/*! \file bits.h
* \brief Osmocom bit level support code
*
* NOTE on the endianess of pbit_t:
* Bits in a pbit_t are ordered MSB first, i.e. 0x80 is the first bit.
* Bit i in a pbit_t array is array[i/8] & (1<<(7-i%8))
*/
typedef int8_t sbit_t; /*!< \brief soft bit (-127...127) */
typedef uint8_t ubit_t; /*!< \brief unpacked bit (0 or 1) */
typedef uint8_t pbit_t; /*!< \brief packed bis (8 bits in a byte) */
/*! \brief determine how many bytes we would need for \a num_bits packed bits
* \param[in] num_bits Number of packed bits
* \returns number of bytes needed for \a num_bits packed bits
*/
static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits)
{
unsigned int pbit_bytesize = num_bits / 8;
if (num_bits % 8)
pbit_bytesize++;
return pbit_bytesize;
}
int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits);
int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits);
void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles);
void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles);
void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits);
void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits);
int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
const ubit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode);
int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
const pbit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode);
#define OSMO_BIN_SPEC "%d%d%d%d%d%d%d%d"
#define OSMO_BIN_PRINT(byte) \
(byte & 0x80 ? 1 : 0), \
(byte & 0x40 ? 1 : 0), \
(byte & 0x20 ? 1 : 0), \
(byte & 0x10 ? 1 : 0), \
(byte & 0x08 ? 1 : 0), \
(byte & 0x04 ? 1 : 0), \
(byte & 0x02 ? 1 : 0), \
(byte & 0x01 ? 1 : 0)
#define OSMO_BIT_SPEC "%c%c%c%c%c%c%c%c"
#define OSMO_BIT_PRINT(byte) \
(byte & 0x80 ? '1' : '.'), \
(byte & 0x40 ? '1' : '.'), \
(byte & 0x20 ? '1' : '.'), \
(byte & 0x10 ? '1' : '.'), \
(byte & 0x08 ? '1' : '.'), \
(byte & 0x04 ? '1' : '.'), \
(byte & 0x02 ? '1' : '.'), \
(byte & 0x01 ? '1' : '.')
/* BIT REVERSAL */
/*! \brief bit-reversal mode for osmo_bit_reversal() */
enum osmo_br_mode {
/*! \brief reverse all bits in a 32bit dword */
OSMO_BR_BITS_IN_DWORD = 31,
/*! \brief reverse byte order in a 32bit dword */
OSMO_BR_BYTES_IN_DWORD = 24,
/*! \brief reverse bits of each byte in a 32bit dword */
OSMO_BR_BITS_IN_BYTE = 7,
/*! \brief swap the two 16bit words in a 32bit dword */
OSMO_BR_WORD_SWAP = 16,
};
/*! \brief generic bit reversal function */
uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k);
/* \brief reverse the bits within each byte of a 32bit word */
uint32_t osmo_revbytebits_32(uint32_t x);
/* \brief reverse the bits within a byte */
uint32_t osmo_revbytebits_8(uint8_t x);
/* \brief reverse the bits of each byte in a given buffer */
void osmo_revbytebits_buf(uint8_t *buf, int len);
/*! \brief left circular shift
* \param[in] in The 16 bit unsigned integer to be rotated
* \param[in] shift Number of bits to shift \a in to, [0;16] bits
* \returns shifted value
*/
static inline uint16_t osmo_rol16(uint16_t in, unsigned shift)
{
return (in << shift) | (in >> (16 - shift));
}
/*! @} */

View File

@@ -0,0 +1,47 @@
#pragma once
/*! \defgroup utils General-purpose utility functions
* @{
*/
/*! \file defs.h
* \brief General definitions that are meant to be included from header files.
*/
/*! \brief Check for gcc and version.
*
* \note Albeit glibc provides a features.h file that contains a similar
* definition (__GNUC_PREREQ), this definition has been copied from there
* to have it available with other libraries, too.
*
* \return != 0 iff gcc is used and it's version is at least maj.min.
*/
#if defined __GNUC__ && defined __GNUC_MINOR__
# define OSMO_GNUC_PREREQ(maj, min) \
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
#else
# define OSMO_GNUC_PREREQ(maj, min) 0
#endif
/*! \brief Set the deprecated attribute with a message.
*/
#if defined(__clang__)
# define _OSMO_HAS_ATTRIBUTE_DEPRECATED __has_attribute(deprecated)
# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE __has_extension(attribute_deprecated_with_message)
#elif defined(__GNUC__)
# define _OSMO_HAS_ATTRIBUTE_DEPRECATED 1
# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE OSMO_GNUC_PREREQ(4,5)
#endif
#if _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
# define OSMO_DEPRECATED(text) __attribute__((__deprecated__(text)))
#elif _OSMO_HAS_ATTRIBUTE_DEPRECATED
# define OSMO_DEPRECATED(text) __attribute__((__deprecated__))
#else
# define OSMO_DEPRECATED(text)
#endif
#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED
/*! @} */

View File

@@ -0,0 +1,510 @@
#pragma once
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/defs.h>
/*! \defgroup msgb Message buffers
* @{
*/
/*! \file msgb.h
* \brief Osmocom message buffers
* The Osmocom message buffers are modelled after the 'struct skb'
* inside the Linux kernel network stack. As they exist in userspace,
* they are much simplified. However, terminology such as headroom,
* tailroom, push/pull/put etc. remains the same.
*/
#define MSGB_DEBUG
/*! \brief Osmocom message buffer */
struct msgb {
struct llist_head list; /*!< \brief linked list header */
/* Part of which TRX logical channel we were received / transmitted */
/* FIXME: move them into the control buffer */
union {
void *dst; /*!< \brief reference of origin/destination */
struct gsm_bts_trx *trx;
};
struct gsm_lchan *lchan; /*!< \brief logical channel */
unsigned char *l1h; /*!< \brief pointer to Layer1 header (if any) */
unsigned char *l2h; /*!< \brief pointer to A-bis layer 2 header: OML, RSL(RLL), NS */
unsigned char *l3h; /*!< \brief pointer to Layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
unsigned char *l4h; /*!< \brief pointer to layer 4 header */
unsigned long cb[5]; /*!< \brief control buffer */
uint16_t data_len; /*!< \brief length of underlying data array */
uint16_t len; /*!< \brief length of bytes used in msgb */
unsigned char *head; /*!< \brief start of underlying memory buffer */
unsigned char *tail; /*!< \brief end of message in buffer */
unsigned char *data; /*!< \brief start of message in buffer */
unsigned char _data[0]; /*!< \brief optional immediate data array */
};
extern struct msgb *msgb_alloc(uint16_t size, const char *name);
extern void msgb_free(struct msgb *m);
extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
extern struct msgb *msgb_dequeue(struct llist_head *queue);
extern void msgb_reset(struct msgb *m);
uint16_t msgb_length(const struct msgb *msg);
extern const char *msgb_hexdump(const struct msgb *msg);
extern int msgb_resize_area(struct msgb *msg, uint8_t *area,
int old_size, int new_size);
extern struct msgb *msgb_copy(const struct msgb *msg, const char *name);
static int msgb_test_invariant(const struct msgb *msg) __attribute__((pure));
#ifdef MSGB_DEBUG
#include <osmocom/core/panic.h>
#define MSGB_ABORT(msg, fmt, args ...) do { \
osmo_panic("msgb(%p): " fmt, msg, ## args); \
} while(0)
#else
#define MSGB_ABORT(msg, fmt, args ...)
#endif
/*! \brief obtain L1 header of msgb */
#define msgb_l1(m) ((void *)(m->l1h))
/*! \brief obtain L2 header of msgb */
#define msgb_l2(m) ((void *)(m->l2h))
/*! \brief obtain L3 header of msgb */
#define msgb_l3(m) ((void *)(m->l3h))
/*! \brief obtain SMS header of msgb */
#define msgb_sms(m) ((void *)(m->l4h))
/*! \brief determine length of L1 message
* \param[in] msgb message buffer
* \returns size of L1 message in bytes
*
* This function computes the number of bytes between the tail of the
* message and the layer 1 header.
*/
static inline unsigned int msgb_l1len(const struct msgb *msgb)
{
return msgb->tail - (uint8_t *)msgb_l1(msgb);
}
/*! \brief determine length of L2 message
* \param[in] msgb message buffer
* \returns size of L2 message in bytes
*
* This function computes the number of bytes between the tail of the
* message and the layer 2 header.
*/
static inline unsigned int msgb_l2len(const struct msgb *msgb)
{
return msgb->tail - (uint8_t *)msgb_l2(msgb);
}
/*! \brief determine length of L3 message
* \param[in] msgb message buffer
* \returns size of L3 message in bytes
*
* This function computes the number of bytes between the tail of the
* message and the layer 3 header.
*/
static inline unsigned int msgb_l3len(const struct msgb *msgb)
{
return msgb->tail - (uint8_t *)msgb_l3(msgb);
}
/*! \brief determine the length of the header
* \param[in] msgb message buffer
* \returns number of bytes between start of buffer and start of msg
*
* This function computes the length difference between the underlying
* data buffer and the used section of the \a msgb.
*/
static inline unsigned int msgb_headlen(const struct msgb *msgb)
{
return msgb->len - msgb->data_len;
}
/*! \brief determine how much tail room is left in msgb
* \param[in] msgb message buffer
* \returns number of bytes remaining at end of msgb
*
* This function computes the amount of octets left in the underlying
* data buffer after the end of the message.
*/
static inline int msgb_tailroom(const struct msgb *msgb)
{
return (msgb->head + msgb->data_len) - msgb->tail;
}
/*! \brief determine the amount of headroom in msgb
* \param[in] msgb message buffer
* \returns number of bytes left ahead of message start in msgb
*
* This function computes the amount of bytes left in the underlying
* data buffer before the start of the actual message.
*/
static inline int msgb_headroom(const struct msgb *msgb)
{
return (msgb->data - msgb->head);
}
/*! \brief append data to end of message buffer
* \param[in] msgb message buffer
* \param[in] len number of bytes to append to message
* \returns pointer to start of newly-appended data
*
* This function will move the \a tail pointer of the message buffer \a
* len bytes further, thus enlarging the message by \a len bytes.
*
* The return value is a pointer to start of the newly added section at
* the end of the message and can be used for actually filling/copying
* data into it.
*/
static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
{
unsigned char *tmp = msgb->tail;
if (msgb_tailroom(msgb) < (int) len)
MSGB_ABORT(msgb, "Not enough tailroom msgb_put (%u < %u)\n",
msgb_tailroom(msgb), len);
msgb->tail += len;
msgb->len += len;
return tmp;
}
/*! \brief append a uint8 value to the end of the message
* \param[in] msgb message buffer
* \param[in] word unsigned 8bit byte to be appended
*/
static inline void msgb_put_u8(struct msgb *msgb, uint8_t word)
{
uint8_t *space = msgb_put(msgb, 1);
space[0] = word & 0xFF;
}
/*! \brief append a uint16 value to the end of the message
* \param[in] msgb message buffer
* \param[in] word unsigned 16bit byte to be appended
*/
static inline void msgb_put_u16(struct msgb *msgb, uint16_t word)
{
uint8_t *space = msgb_put(msgb, 2);
osmo_store16be(word, space);
}
/*! \brief append a uint32 value to the end of the message
* \param[in] msgb message buffer
* \param[in] word unsigned 32bit byte to be appended
*/
static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
{
uint8_t *space = msgb_put(msgb, 4);
osmo_store32be(word, space);
}
/*! \brief remove data from end of message
* \param[in] msgb message buffer
* \param[in] len number of bytes to remove from end
*/
static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
{
unsigned char *tmp = msgb->tail - len;
if (msgb_length(msgb) < len)
MSGB_ABORT(msgb, "msgb too small to get %u (len %u)\n",
len, msgb_length(msgb));
msgb->tail -= len;
msgb->len -= len;
return tmp;
}
/*! \brief remove uint8 from end of message
* \param[in] msgb message buffer
* \returns 8bit value taken from end of msgb
*/
static inline uint8_t msgb_get_u8(struct msgb *msgb)
{
uint8_t *space = msgb_get(msgb, 1);
return space[0];
}
/*! \brief remove uint16 from end of message
* \param[in] msgb message buffer
* \returns 16bit value taken from end of msgb
*/
static inline uint16_t msgb_get_u16(struct msgb *msgb)
{
uint8_t *space = msgb_get(msgb, 2);
return osmo_load16be(space);
}
/*! \brief remove uint32 from end of message
* \param[in] msgb message buffer
* \returns 32bit value taken from end of msgb
*/
static inline uint32_t msgb_get_u32(struct msgb *msgb)
{
uint8_t *space = msgb_get(msgb, 4);
return osmo_load32be(space);
}
/*! \brief prepend (push) some data to start of message
* \param[in] msgb message buffer
* \param[in] len number of bytes to pre-pend
* \returns pointer to newly added portion at start of \a msgb
*
* This function moves the \a data pointer of the \ref msgb further
* to the front (by \a len bytes), thereby enlarging the message by \a
* len bytes.
*
* The return value is a pointer to the newly added section in the
* beginning of the message. It can be used to fill/copy data into it.
*/
static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
{
if (msgb_headroom(msgb) < (int) len)
MSGB_ABORT(msgb, "Not enough headroom msgb_push (%u < %u)\n",
msgb_headroom(msgb), len);
msgb->data -= len;
msgb->len += len;
return msgb->data;
}
/*! \brief prepend a uint8 value to the head of the message
* \param[in] msgb message buffer
* \param[in] word unsigned 8bit byte to be prepended
*/
static inline void msgb_push_u8(struct msgb *msg, uint8_t word)
{
uint8_t *space = msgb_push(msg, 1);
space[0] = word;
}
/*! \brief prepend a uint16 value to the head of the message
* \param[in] msgb message buffer
* \param[in] word unsigned 16bit byte to be prepended
*/
static inline void msgb_push_u16(struct msgb *msg, uint16_t word)
{
uint16_t *space = (uint16_t *) msgb_push(msg, 2);
osmo_store16be(word, space);
}
/*! \brief prepend a uint32 value to the head of the message
* \param[in] msgb message buffer
* \param[in] word unsigned 32bit byte to be prepended
*/
static inline void msgb_push_u32(struct msgb *msg, uint32_t word)
{
uint32_t *space = (uint32_t *) msgb_push(msg, 4);
osmo_store32be(word, space);
}
/*! \brief remove (pull) a header from the front of the message buffer
* \param[in] msgb message buffer
* \param[in] len number of octets to be pulled
* \returns pointer to new start of msgb
*
* This function moves the \a data pointer of the \ref msgb further back
* in the message, thereby shrinking the size of the message by \a len
* bytes.
*/
static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
{
msgb->len -= len;
return msgb->data += len;
}
/*! \brief remove (pull) all headers in front of l3h from the message buffer.
* \param[in] msgb message buffer with a valid l3h
* \returns pointer to new start of msgb (l3h)
*
* This function moves the \a data pointer of the \ref msgb further back
* in the message, thereby shrinking the size of the message.
* l1h and l2h will be cleared.
*/
static inline unsigned char *msgb_pull_to_l3(struct msgb *msg)
{
unsigned char *ret = msgb_pull(msg, msg->l3h - msg->data);
msg->l1h = msg->l2h = NULL;
return ret;
}
/*! \brief remove (pull) all headers in front of l2h from the message buffer.
* \param[in] msgb message buffer with a valid l2h
* \returns pointer to new start of msgb (l2h)
*
* This function moves the \a data pointer of the \ref msgb further back
* in the message, thereby shrinking the size of the message.
* l1h will be cleared.
*/
static inline unsigned char *msgb_pull_to_l2(struct msgb *msg)
{
unsigned char *ret = msgb_pull(msg, msg->l2h - msg->data);
msg->l1h = NULL;
return ret;
}
/*! \brief remove uint8 from front of message
* \param[in] msgb message buffer
* \returns 8bit value taken from end of msgb
*/
static inline uint8_t msgb_pull_u8(struct msgb *msgb)
{
uint8_t *space = msgb_pull(msgb, 1) - 1;
return space[0];
}
/*! \brief remove uint16 from front of message
* \param[in] msgb message buffer
* \returns 16bit value taken from end of msgb
*/
static inline uint16_t msgb_pull_u16(struct msgb *msgb)
{
uint8_t *space = msgb_pull(msgb, 2) - 2;
return osmo_load16be(space);
}
/*! \brief remove uint32 from front of message
* \param[in] msgb message buffer
* \returns 32bit value taken from end of msgb
*/
static inline uint32_t msgb_pull_u32(struct msgb *msgb)
{
uint8_t *space = msgb_pull(msgb, 4) - 4;
return osmo_load32be(space);
}
/*! \brief Increase headroom of empty msgb, reducing the tailroom
* \param[in] msg message buffer
* \param[in] len amount of extra octets to be reserved as headroom
*
* This function reserves some memory at the beginning of the underlying
* data buffer. The idea is to reserve space in case further headers
* have to be pushed to the \ref msgb during further processing.
*
* Calling this function leads to undefined reusults if it is called on
* a non-empty \ref msgb.
*/
static inline void msgb_reserve(struct msgb *msg, int len)
{
msg->data += len;
msg->tail += len;
}
/*! \brief Trim the msgb to a given absolute length
* \param[in] msg message buffer
* \param[in] len new total length of buffer
* \returns 0 in case of success, negative in case of error
*/
static inline int msgb_trim(struct msgb *msg, int len)
{
if (len < 0)
MSGB_ABORT(msg, "Negative length is not allowed\n");
if (len > msg->data_len)
return -1;
msg->len = len;
msg->tail = msg->data + len;
return 0;
}
/*! \brief Trim the msgb to a given layer3 length
* \param[in] msg message buffer
* \param[in] l3len new layer3 length
* \returns 0 in case of success, negative in case of error
*/
static inline int msgb_l3trim(struct msgb *msg, int l3len)
{
return msgb_trim(msg, (msg->l3h - msg->data) + l3len);
}
/*! \brief Allocate message buffer with specified headroom
* \param[in] size size in bytes, including headroom
* \param[in] headroom headroom in bytes
* \param[in] name human-readable name
* \returns allocated message buffer with specified headroom
*
* This function is a convenience wrapper around \ref msgb_alloc
* followed by \ref msgb_reserve in order to create a new \ref msgb with
* user-specified amount of headroom.
*/
static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
const char *name)
{
osmo_static_assert(size > headroom, headroom_bigger);
struct msgb *msg = msgb_alloc(size, name);
if (msg)
msgb_reserve(msg, headroom);
return msg;
}
/*! \brief Check a message buffer for consistency
* \param[in] msg message buffer
* \returns 0 (false) if inconsistent, != 0 (true) otherwise
*/
static inline int msgb_test_invariant(const struct msgb *msg)
{
const unsigned char *lbound;
if (!msg || !msg->data || !msg->tail ||
(msg->data + msg->len != msg->tail) ||
(msg->data < msg->head) ||
(msg->tail > msg->head + msg->data_len))
return 0;
lbound = msg->head;
if (msg->l1h) {
if (msg->l1h < lbound)
return 0;
lbound = msg->l1h;
}
if (msg->l2h) {
if (msg->l2h < lbound)
return 0;
lbound = msg->l2h;
}
if (msg->l3h) {
if (msg->l3h < lbound)
return 0;
lbound = msg->l3h;
}
if (msg->l4h) {
if (msg->l4h < lbound)
return 0;
lbound = msg->l4h;
}
return lbound <= msg->head + msg->data_len;
}
/* non inline functions to ease binding */
uint8_t *msgb_data(const struct msgb *msg);
void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size);
void msgb_set_talloc_ctx(void *ctx) OSMO_DEPRECATED("Use msgb_talloc_ctx_init() instead");
/*! @} */

View File

@@ -0,0 +1,17 @@
#pragma once
/*! \addtogroup utils
* @{
*/
/*! \file panic.h */
#include <stdarg.h>
/*! \brief panic handler callback function type */
typedef void (*osmo_panic_handler_t)(const char *fmt, va_list args);
extern void osmo_panic(const char *fmt, ...);
extern void osmo_set_panic_handler(osmo_panic_handler_t h);
/*! @} */

View File

@@ -0,0 +1,4 @@
/* Convenience wrapper. libosmocore used to ship its own internal copy of
* talloc, before libtalloc became a standard component on most systems */
#pragma once
#include <talloc.h>

View File

@@ -0,0 +1,92 @@
#pragma once
#include <osmocom/core/backtrace.h>
#include <osmocom/core/talloc.h>
/*! \defgroup utils General-purpose utility functions
* @{
*/
/*! \file utils.h */
/*! \brief Determine number of elements in an array of static size */
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/*! \brief Return the maximum of two specified values */
#define OSMO_MAX(a, b) ((a) >= (b) ? (a) : (b))
/*! \brief Return the minimum of two specified values */
#define OSMO_MIN(a, b) ((a) >= (b) ? (b) : (a))
/*! \brief Stringify the contents of a macro, e.g. a port number */
#define OSMO_STRINGIFY(x) #x
/*! \brief Make a value_string entry from an enum value name */
#define OSMO_VALUE_STRING(x) { x, #x }
#include <stdint.h>
#include <stdio.h>
/*! \brief A mapping between human-readable string and numeric value */
struct value_string {
unsigned int value; /*!< \brief numeric value */
const char *str; /*!< \brief human-readable string */
};
const char *get_value_string(const struct value_string *vs, uint32_t val);
const char *get_value_string_or_null(const struct value_string *vs,
uint32_t val);
int get_string_value(const struct value_string *vs, const char *str);
char osmo_bcd2char(uint8_t bcd);
/* only works for numbers in ascci */
uint8_t osmo_char2bcd(char c);
int osmo_hexparse(const char *str, uint8_t *b, int max_len);
char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
char *osmo_hexdump(const unsigned char *buf, int len);
char *osmo_hexdump_nospc(const unsigned char *buf, int len);
char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) __attribute__((__deprecated__));
#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1] __attribute__((__unused__));
void osmo_str2lower(char *out, const char *in);
void osmo_str2upper(char *out, const char *in);
#define OSMO_SNPRINTF_RET(ret, rem, offset, len) \
do { \
len += ret; \
if (ret > rem) \
ret = rem; \
offset += ret; \
rem -= ret; \
} while (0)
/*! Helper macro to terminate when an assertion failes
* \param[in] exp Predicate to verify
* This function will generate a backtrace and terminate the program if
* the predicate evaluates to false (0).
*/
#define OSMO_ASSERT(exp) \
if (!(exp)) { \
fprintf(stderr, "Assert failed %s %s:%d\n", #exp, __BASE_FILE__, __LINE__); \
osmo_generate_backtrace(); \
abort(); \
}
/*! duplicate a string using talloc and release its prior content (if any)
* \param[in] ctx Talloc context to use for allocation
* \param[out] dst pointer to string, will be updated with ptr to new string
* \param[in] newstr String that will be copieed to newly allocated string */
static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char *newstr)
{
if (*dst)
talloc_free(*dst);
*dst = talloc_strdup(ctx, newstr);
}
int osmo_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count);
uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len);
uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len);
size_t osmo_strlcpy(char *dst, const char *src, size_t siz);
/*! @} */

View File

@@ -0,0 +1,92 @@
/*
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2012 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*! \file backtrace.c
* \brief Routines realted to generating call back traces
*/
#include <stdio.h>
#include <stdlib.h>
#include <osmocom/core/utils.h>
//#include <osmocom/core/logging.h>
//#include "config.h"
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
static void _osmo_backtrace(int use_printf, int subsys, int level)
{
int i, nptrs;
void *buffer[100];
char **strings;
nptrs = backtrace(buffer, ARRAY_SIZE(buffer));
if (use_printf)
printf("backtrace() returned %d addresses\n", nptrs);
else
LOGP(subsys, level, "backtrace() returned %d addresses\n",
nptrs);
strings = backtrace_symbols(buffer, nptrs);
if (!strings)
return;
for (i = 1; i < nptrs; i++) {
if (use_printf)
printf("%s\n", strings[i]);
else
LOGP(subsys, level, "\t%s\n", strings[i]);
}
free(strings);
}
/*! \brief Generate and print a call back-trace
*
* This function will generate a function call back-trace of the
* current process and print it to stdout. */
void osmo_generate_backtrace(void)
{
_osmo_backtrace(1, 0, 0);
}
/*! \brief Generate and log a call back-trace
* \param[in] subsys Logging sub-system
* \param[in] level Logging level
*
* This function will generate a function call back-trace of the
* current process and log it to the specified subsystem and
* level using the libosmocore logging subsystem */
void osmo_log_backtrace(int subsys, int level)
{
_osmo_backtrace(0, subsys, level);
}
#else
void osmo_generate_backtrace(void)
{
printf("This platform has no backtrace function\n");
}
void osmo_log_backtrace(int subsys, int level)
{
//LOGP(subsys, level, "This platform has no backtrace function\n");
}
#endif

View File

@@ -0,0 +1,307 @@
/*
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
* (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdint.h>
#include <osmocom/core/bits.h>
/*! \addtogroup bits
* @{
*/
/*! \file bits.c
* \brief Osmocom bit level support code
*/
/*! \brief convert unpacked bits to packed bits, return length in bytes
* \param[out] out output buffer of packed bits
* \param[in] in input buffer of unpacked bits
* \param[in] num_bits number of bits
*/
int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits)
{
unsigned int i;
uint8_t curbyte = 0;
pbit_t *outptr = out;
for (i = 0; i < num_bits; i++) {
uint8_t bitnum = 7 - (i % 8);
curbyte |= (in[i] << bitnum);
if(i % 8 == 7){
*outptr++ = curbyte;
curbyte = 0;
}
}
/* we have a non-modulo-8 bitcount */
if (i % 8)
*outptr++ = curbyte;
return outptr - out;
}
/*! \brief Shift unaligned input to octet-aligned output
* \param[out] out output buffer, unaligned
* \param[in] in input buffer, octet-aligned
* \param[in] num_nibbles number of nibbles
*/
void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles)
{
unsigned int i, num_whole_bytes = num_nibbles / 2;
if (!num_whole_bytes)
return;
/* first byte: upper nibble empty, lower nibble from src */
out[0] = (in[0] >> 4);
/* bytes 1.. */
for (i = 1; i < num_whole_bytes; i++)
out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
/* shift the last nibble, in case there's an odd count */
i = num_whole_bytes;
if (num_nibbles & 1)
out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
else
out[i] = (in[i - 1] & 0xF) << 4;
}
/*! \brief Shift unaligned input to octet-aligned output
* \param[out] out output buffer, octet-aligned
* \param[in] in input buffer, unaligned
* \param[in] num_nibbles number of nibbles
*/
void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
unsigned int num_nibbles)
{
unsigned int i, num_whole_bytes = num_nibbles / 2;
if (!num_whole_bytes)
return;
for (i = 0; i < num_whole_bytes; i++)
out[i] = ((in[i] & 0xF) << 4) | (in[i + 1] >> 4);
/* shift the last nibble, in case there's an odd count */
i = num_whole_bytes;
if (num_nibbles & 1)
out[i] = (in[i] & 0xF) << 4;
}
/*! \brief convert unpacked bits to soft bits
* \param[out] out output buffer of soft bits
* \param[in] in input buffer of unpacked bits
* \param[in] num_bits number of bits
*/
void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits)
{
unsigned int i;
for (i = 0; i < num_bits; i++)
out[i] = in[i] ? -127 : 127;
}
/*! \brief convert soft bits to unpacked bits
* \param[out] out output buffer of unpacked bits
* \param[in] in input buffer of soft bits
* \param[in] num_bits number of bits
*/
void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits)
{
unsigned int i;
for (i = 0; i < num_bits; i++)
out[i] = in[i] < 0;
}
/*! \brief convert packed bits to unpacked bits, return length in bytes
* \param[out] out output buffer of unpacked bits
* \param[in] in input buffer of packed bits
* \param[in] num_bits number of bits
* \return number of bytes used in \ref out
*/
int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits)
{
unsigned int i;
ubit_t *cur = out;
ubit_t *limit = out + num_bits;
for (i = 0; i < (num_bits/8)+1; i++) {
pbit_t byte = in[i];
*cur++ = (byte >> 7) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 6) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 5) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 4) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 3) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 2) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 1) & 1;
if (cur >= limit)
break;
*cur++ = (byte >> 0) & 1;
if (cur >= limit)
break;
}
return cur - out;
}
/*! \brief convert unpacked bits to packed bits (extended options)
* \param[out] out output buffer of packed bits
* \param[in] out_ofs offset into output buffer
* \param[in] in input buffer of unpacked bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
* \param[in] lsb_mode Encode bits in LSB orde instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
const ubit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
int i, op, bn;
for (i=0; i<num_bits; i++) {
op = out_ofs + i;
bn = lsb_mode ? (op&7) : (7-(op&7));
if (in[in_ofs+i])
out[op>>3] |= 1 << bn;
else
out[op>>3] &= ~(1 << bn);
}
return ((out_ofs + num_bits - 1) >> 3) + 1;
}
/*! \brief convert packed bits to unpacked bits (extended options)
* \param[out] out output buffer of unpacked bits
* \param[in] out_ofs offset into output buffer
* \param[in] in input buffer of packed bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
* \param[in] lsb_mode Encode bits in LSB orde instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
const pbit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
int i, ip, bn;
for (i=0; i<num_bits; i++) {
ip = in_ofs + i;
bn = lsb_mode ? (ip&7) : (7-(ip&7));
out[out_ofs+i] = !!(in[ip>>3] & (1<<bn));
}
return out_ofs + num_bits;
}
/*! \brief generalized bit reversal function
* \param[in] x the 32bit value to be reversed
* \param[in] k the type of reversal requested
* \returns the reversed 32bit dword
*
* This function reverses the bit order within a 32bit word. Depending
* on "k", it either reverses all bits in a 32bit dword, or the bytes in
* the dword, or the bits in each byte of a dword, or simply swaps the
* two 16bit words in a dword. See Chapter 7 "Hackers Delight"
*/
uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k)
{
if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
return x;
}
/*! \brief reverse the bit-order in each byte of a dword
* \param[in] x 32bit input value
* \returns 32bit value where bits of each byte have been reversed
*
* See Chapter 7 "Hackers Delight"
*/
uint32_t osmo_revbytebits_32(uint32_t x)
{
x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
return x;
}
/*! \brief reverse the bit order in a byte
* \param[in] x 8bit input value
* \returns 8bit value where bits order has been reversed
*
* See Chapter 7 "Hackers Delight"
*/
uint32_t osmo_revbytebits_8(uint8_t x)
{
x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
x = (x & 0x33) << 2 | (x & 0xCC) >> 2;
x = (x & 0x0F) << 4 | (x & 0xF0) >> 4;
return x;
}
/*! \brief reverse bit-order of each byte in a buffer
* \param[in] buf buffer containing bytes to be bit-reversed
* \param[in] len length of buffer in bytes
*
* This function reverses the bits in each byte of the buffer
*/
void osmo_revbytebits_buf(uint8_t *buf, int len)
{
unsigned int i;
unsigned int unaligned_cnt;
int len_remain = len;
unaligned_cnt = ((unsigned long)buf & 3);
for (i = 0; i < unaligned_cnt; i++) {
buf[i] = osmo_revbytebits_8(buf[i]);
len_remain--;
if (len_remain <= 0)
return;
}
for (i = unaligned_cnt; i + 3 < len; i += 4) {
osmo_store32be(osmo_revbytebits_32(osmo_load32be(buf + i)), buf + i);
len_remain -= 4;
}
for (i = len - len_remain; i < len; i++) {
buf[i] = osmo_revbytebits_8(buf[i]);
len_remain--;
}
}
/*! @} */

View File

@@ -0,0 +1,353 @@
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*! \addtogroup msgb
* @{
*/
/*! \file msgb.c
*/
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <osmocom/core/msgb.h>
//#include <openbsc/gsm_data.h>
#include <osmocom/core/talloc.h>
//#include <openbsc/debug.h>
void *tall_msgb_ctx = NULL;
/*! \brief Allocate a new message buffer
* \param[in] size Length in octets, including headroom
* \param[in] name Human-readable name to be associated with msgb
* \returns dynamically-allocated \ref msgb
*
* This function allocates a 'struct msgb' as well as the underlying
* memory buffer for the actual message data (size specified by \a size)
* using the talloc memory context previously set by \ref msgb_set_talloc_ctx
*/
struct msgb *msgb_alloc(uint16_t size, const char *name)
{
struct msgb *msg;
msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name);
if (!msg) {
//LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n");
return NULL;
}
msg->data_len = size;
msg->len = 0;
msg->data = msg->_data;
msg->head = msg->_data;
msg->tail = msg->_data;
return msg;
}
/*! \brief Release given message buffer
* \param[in] m Message buffer to be free'd
*/
void msgb_free(struct msgb *m)
{
talloc_free(m);
}
/*! \brief Enqueue message buffer to tail of a queue
* \param[in] queue linked list header of queue
* \param[in] msg message buffer to be added to the queue
*
* The function will append the specified message buffer \a msg to the
* queue implemented by \ref llist_head \a queue
*/
void msgb_enqueue(struct llist_head *queue, struct msgb *msg)
{
llist_add_tail(&msg->list, queue);
}
/*! \brief Dequeue message buffer from head of queue
* \param[in] queue linked list header of queue
* \returns message buffer (if any) or NULL if queue empty
*
* The function will remove the first message buffer from the queue
* implemented by \ref llist_head \a queue.
*/
struct msgb *msgb_dequeue(struct llist_head *queue)
{
struct llist_head *lh;
if (llist_empty(queue))
return NULL;
lh = queue->next;
if (lh) {
llist_del(lh);
return llist_entry(lh, struct msgb, list);
} else
return NULL;
}
/*! \brief Re-set all message buffer pointers
* \param[in] msg message buffer that is to be resetted
*
* This will re-set the various internal pointers into the underlying
* message buffer, i.e. remvoe all headroom and treat the msgb as
* completely empty. It also initializes the control buffer to zero.
*/
void msgb_reset(struct msgb *msg)
{
msg->len = 0;
msg->data = msg->_data;
msg->head = msg->_data;
msg->tail = msg->_data;
msg->trx = NULL;
msg->lchan = NULL;
msg->l2h = NULL;
msg->l3h = NULL;
msg->l4h = NULL;
memset(&msg->cb, 0, sizeof(msg->cb));
}
/*! \brief get pointer to data section of message buffer
* \param[in] msg message buffer
* \returns pointer to data section of message buffer
*/
uint8_t *msgb_data(const struct msgb *msg)
{
return msg->data;
}
/*! \brief get length of message buffer
* \param[in] msg message buffer
* \returns length of data section in message buffer
*/
uint16_t msgb_length(const struct msgb *msg)
{
return msg->len;
}
/*! \brief Set the talloc context for \ref msgb_alloc
* Deprecated, use msgb_talloc_ctx_init() instead.
* \param[in] ctx talloc context to be used as root for msgb allocations
*/
void msgb_set_talloc_ctx(void *ctx)
{
tall_msgb_ctx = ctx;
}
/*! \brief Initialize a msgb talloc context for \ref msgb_alloc.
* Create a talloc context called "msgb". If \a pool_size is 0, create a named
* const as msgb talloc context. If \a pool_size is nonzero, create a talloc
* pool, possibly for faster msgb allocations (see talloc_pool()).
* \param[in] root_ctx talloc context used as parent for the new "msgb" ctx.
* \param[in] pool_size if nonzero, create a talloc pool of this size.
* \returns the new msgb talloc context, e.g. for reporting
*/
void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size)
{
if (!pool_size)
tall_msgb_ctx = talloc_size(root_ctx, 0);
else
tall_msgb_ctx = talloc_pool(root_ctx, pool_size);
talloc_set_name_const(tall_msgb_ctx, "msgb");
return tall_msgb_ctx;
}
/*! \brief Copy an msgb.
*
* This function allocates a new msgb, copies the data buffer of msg,
* and adjusts the pointers (incl l1h-l4h) accordingly. The cb part
* is not copied.
* \param[in] msg The old msgb object
* \param[in] name Human-readable name to be associated with msgb
*/
struct msgb *msgb_copy(const struct msgb *msg, const char *name)
{
struct msgb *new_msg;
new_msg = msgb_alloc(msg->data_len, name);
if (!new_msg)
return NULL;
/* copy data */
memcpy(new_msg->_data, msg->_data, new_msg->data_len);
/* copy header */
new_msg->len = msg->len;
new_msg->data += msg->data - msg->_data;
new_msg->head += msg->head - msg->_data;
new_msg->tail += msg->tail - msg->_data;
if (msg->l1h)
new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
if (msg->l2h)
new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data);
if (msg->l3h)
new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data);
if (msg->l4h)
new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data);
return new_msg;
}
/*! \brief Resize an area within an msgb
*
* This resizes a sub area of the msgb data and adjusts the pointers (incl
* l1h-l4h) accordingly. The cb part is not updated. If the area is extended,
* the contents of the extension is undefined. The complete sub area must be a
* part of [data,tail].
*
* \param[inout] msg The msgb object
* \param[in] area A pointer to the sub-area
* \param[in] old_size The old size of the sub-area
* \param[in] new_size The new size of the sub-area
* \returns 0 on success, -1 if there is not enough space to extend the area
*/
int msgb_resize_area(struct msgb *msg, uint8_t *area,
int old_size, int new_size)
{
int rc;
uint8_t *post_start = area + old_size;
int pre_len = area - msg->data;
int post_len = msg->len - old_size - pre_len;
int delta_size = new_size - old_size;
if (old_size < 0 || new_size < 0)
MSGB_ABORT(msg, "Negative sizes are not allowed\n");
if (area < msg->data || post_start > msg->tail)
MSGB_ABORT(msg, "Sub area is not fully contained in the msg data\n");
if (delta_size == 0)
return 0;
if (delta_size > 0) {
rc = msgb_trim(msg, msg->len + delta_size);
if (rc < 0)
return rc;
}
memmove(area + new_size, area + old_size, post_len);
if (msg->l1h >= post_start)
msg->l1h += delta_size;
if (msg->l2h >= post_start)
msg->l2h += delta_size;
if (msg->l3h >= post_start)
msg->l3h += delta_size;
if (msg->l4h >= post_start)
msg->l4h += delta_size;
if (delta_size < 0)
msgb_trim(msg, msg->len + delta_size);
return 0;
}
/*! \brief Return a (static) buffer containing a hexdump of the msg
* \param[in] msg message buffer
* \returns a pointer to a static char array
*/
const char *msgb_hexdump(const struct msgb *msg)
{
static char buf[4100];
int buf_offs = 0;
int nchars;
const unsigned char *start = msg->data;
const unsigned char *lxhs[4];
int i;
lxhs[0] = msg->l1h;
lxhs[1] = msg->l2h;
lxhs[2] = msg->l3h;
lxhs[3] = msg->l4h;
for (i = 0; i < ARRAY_SIZE(lxhs); i++) {
if (!lxhs[i])
continue;
if (lxhs[i] < msg->head)
continue;
if (lxhs[i] > msg->head + msg->data_len)
continue;
if (lxhs[i] > msg->tail)
continue;
if (lxhs[i] < msg->data || lxhs[i] > msg->tail) {
nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
"(L%d=data%+" PRIdPTR ") ",
i+1, lxhs[i] - msg->data);
buf_offs += nchars;
continue;
}
if (lxhs[i] < start) {
nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
"(L%d%+" PRIdPTR ") ", i+1,
start - lxhs[i]);
buf_offs += nchars;
continue;
}
nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
"%s[L%d]> ",
osmo_hexdump(start, lxhs[i] - start),
i+1);
if (nchars < 0 || nchars + buf_offs >= sizeof(buf))
return "ERROR";
buf_offs += nchars;
start = lxhs[i];
}
nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
"%s", osmo_hexdump(start, msg->tail - start));
if (nchars < 0 || nchars + buf_offs >= sizeof(buf))
return "ERROR";
buf_offs += nchars;
for (i = 0; i < ARRAY_SIZE(lxhs); i++) {
if (!lxhs[i])
continue;
if (lxhs[i] < msg->head || lxhs[i] > msg->head + msg->data_len) {
nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
"(L%d out of range) ", i+1);
} else if (lxhs[i] <= msg->data + msg->data_len &&
lxhs[i] > msg->tail) {
nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs,
"(L%d=tail%+" PRIdPTR ") ",
i+1, lxhs[i] - msg->tail);
} else
continue;
if (nchars < 0 || nchars + buf_offs >= sizeof(buf))
return "ERROR";
buf_offs += nchars;
}
return buf;
}
/*! @} */

View File

@@ -0,0 +1,102 @@
/* Panic handling */
/*
* (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*! \addtogroup utils
* @{
*/
/*! \file panic.c
* \brief Routines for panic handling
*/
#include <assert.h>
#include <osmocom/core/panic.h>
#include <osmocom/core/backtrace.h>
//#include "../config.h"
static osmo_panic_handler_t osmo_panic_handler = (void*)0;
#ifndef PANIC_INFLOOP
#include <stdio.h>
#include <stdlib.h>
static void osmo_panic_default(const char *fmt, va_list args)
{
vfprintf(stderr, fmt, args);
osmo_generate_backtrace();
assert(0);
}
#else
static void osmo_panic_default(const char *fmt, va_list args)
{
while (1);
}
#endif
/*! \brief Terminate the current program with a panic
*
* You can call this function in case some severely unexpected situation
* is detected and the program is supposed to terminate in a way that
* reports the fact that it terminates.
*
* The application can register a panic handler function using \ref
* osmo_set_panic_handler. If it doesn't, a default panic handler
* function is called automatically.
*
* The default function on most systems will generate a backtrace and
* then abort() the process.
*/
void osmo_panic(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (osmo_panic_handler)
osmo_panic_handler(fmt, args);
else
osmo_panic_default(fmt, args);
va_end(args);
}
/*! \brief Set the panic handler
* \param[in] h New panic handler function
*
* This changes the panic handling function from the currently active
* function to a new call-back function supplied by the caller.
*/
void osmo_set_panic_handler(osmo_panic_handler_t h)
{
osmo_panic_handler = h;
}
/*! @} */

View File

@@ -1,8 +1,9 @@
CFLAGS=-g -Wall -I../src_simtrace -I../libcommon/include -I. CFLAGS=-g -Wall -I../src_simtrace -I../libcommon/include -I.
LDFLAGS=-losmocore
VPATH=../src_simtrace ../libcommon/source VPATH=../src_simtrace ../libcommon/source
card_emu_test: card_emu_tests.hobj card_emu.hobj req_ctx.hobj iso7816_fidi.hobj card_emu_test: card_emu_tests.hobj card_emu.hobj usb_buf.hobj iso7816_fidi.hobj
$(CC) $(LDFLAGS) -o $@ $^ $(CC) $(LDFLAGS) -o $@ $^
%.hobj: %.c %.hobj: %.c

View File

@@ -5,9 +5,13 @@
#include <stdlib.h> #include <stdlib.h>
#include "card_emu.h" #include "card_emu.h"
#include "cardemu_prot.h" #include "simtrace_prot.h"
#include "tc_etu.h" #include "tc_etu.h"
#include "req_ctx.h" #include "usb_buf.h"
#define PHONE_DATAIN 1
#define PHONE_INT 2
#define PHONE_DATAOUT 3
/* stub functions required by card_emu.c */ /* stub functions required by card_emu.c */
@@ -128,21 +132,23 @@ static void reader_send_bytes(struct card_handle *ch, const uint8_t *bytes, unsi
} }
} }
static void dump_rctx(struct req_ctx *rctx) static void dump_rctx(struct msgb *msg)
{ {
struct cardemu_usb_msg_hdr *mh = struct simtrace_msg_hdr *mh = (struct simtrace_msg_hdr *) msg->l1h;
(struct cardemu_usb_msg_hdr *) rctx->data;
struct cardemu_usb_msg_rx_data *rxd; struct cardemu_usb_msg_rx_data *rxd;
int i; int i;
#if 0
printf("req_ctx(%p): state=%u, size=%u, tot_len=%u, idx=%u, data=%p\n", printf("req_ctx(%p): state=%u, size=%u, tot_len=%u, idx=%u, data=%p\n",
rctx, rctx->state, rctx->size, rctx->tot_len, rctx->idx, rctx->data); rctx, rctx->state, rctx->size, rctx->tot_len, rctx->idx, rctx->data);
printf(" msg_type=%u, seq_nr=%u, msg_len=%u\n", printf(" msg_type=%u, seq_nr=%u, msg_len=%u\n",
mh->msg_type, mh->seq_nr, mh->msg_len); mh->msg_type, mh->seq_nr, mh->msg_len);
#endif
printf("%s\n", msgb_hexdump(msg));
switch (mh->msg_type) { switch (mh->msg_type) {
case CEMU_USB_MSGT_DO_RX_DATA: case SIMTRACE_MSGT_DO_CEMU_RX_DATA:
rxd = (struct cardemu_usb_msg_rx_data *)mh; rxd = (struct cardemu_usb_msg_rx_data *) msg->l2h;
printf(" flags=%x, data=", rxd->flags); printf(" flags=%x, data=", rxd->flags);
for (i = 0; i < rxd->data_len; i++) for (i = 0; i < rxd->data_len; i++)
printf(" %02x", rxd->data[i]); printf(" %02x", rxd->data[i]);
@@ -151,27 +157,31 @@ static void dump_rctx(struct req_ctx *rctx)
} }
} }
static void get_and_verify_rctx(int state, const uint8_t *data, unsigned int len) static void get_and_verify_rctx(uint8_t ep, const uint8_t *data, unsigned int len)
{ {
struct req_ctx *rctx; struct llist_head *queue = usb_get_queue(ep);
struct msgb *msg;
struct cardemu_usb_msg_tx_data *td; struct cardemu_usb_msg_tx_data *td;
struct cardemu_usb_msg_rx_data *rd; struct cardemu_usb_msg_rx_data *rd;
struct simtrace_msg_hdr *mh;
rctx = req_ctx_find_get(0, state, RCTX_S_USB_TX_BUSY); assert(queue);
assert(rctx); msg = msgb_dequeue(queue);
dump_rctx(rctx); assert(msg);
dump_rctx(msg);
assert(msg->l1h);
mh = (struct simtrace_msg_hdr *) msg->l1h;
/* verify the contents of the rctx */ /* verify the contents of the rctx */
switch (state) { switch (mh->msg_type) {
case RCTX_S_USB_TX_PENDING: case SIMTRACE_MSGT_DO_CEMU_RX_DATA:
td = (struct cardemu_usb_msg_tx_data *) rctx->data; rd = (struct cardemu_usb_msg_rx_data *) msg->l2h;
assert(td->hdr.msg_type == CEMU_USB_MSGT_DO_RX_DATA); assert(rd->data_len == len);
assert(td->data_len == len); assert(!memcmp(rd->data, data, len));
assert(!memcmp(td->data, data, len));
break; break;
#if 0 #if 0
case RCTX_S_UART_RX_PENDING: case RCTX_S_UART_RX_PENDING:
rd = (struct cardemu_usb_msg_rx_data *) rctx->data; rd = (struct cardemu_usb_msg_rx_data *) msg->l2h;
assert(rd->data_len == len); assert(rd->data_len == len);
assert(!memcmp(rd->data, data, len)); assert(!memcmp(rd->data, data, len));
break; break;
@@ -181,64 +191,81 @@ static void get_and_verify_rctx(int state, const uint8_t *data, unsigned int len
} }
/* free the req_ctx, indicating it has fully arrived on the host */ /* free the req_ctx, indicating it has fully arrived on the host */
req_ctx_set_state(rctx, RCTX_S_FREE); usb_buf_free(msg);
} }
static void get_and_verify_rctx_pps(const uint8_t *data, unsigned int len) static void get_and_verify_rctx_pps(const uint8_t *data, unsigned int len)
{ {
struct req_ctx *rctx; struct llist_head *queue = usb_get_queue(PHONE_DATAIN);
struct msgb *msg;
struct simtrace_msg_hdr *mh;
struct cardemu_usb_msg_pts_info *ptsi; struct cardemu_usb_msg_pts_info *ptsi;
rctx = req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY); assert(queue);
assert(rctx); msg = msgb_dequeue(queue);
dump_rctx(rctx); assert(msg);
dump_rctx(msg);
assert(msg->l1h);
mh = (struct simtrace_msg_hdr *) msg->l1h;
ptsi = (struct cardemu_usb_msg_pts_info *) msg->l2h;
ptsi = (struct cardemu_usb_msg_pts_info *) rctx->data;
/* FIXME: verify */ /* FIXME: verify */
assert(ptsi->hdr.msg_type == CEMU_USB_MSGT_DO_PTS); assert(mh->msg_type == SIMTRACE_MSGT_DO_CEMU_PTS);
assert(!memcmp(ptsi->req, data, len)); assert(!memcmp(ptsi->req, data, len));
assert(!memcmp(ptsi->resp, data, len)); assert(!memcmp(ptsi->resp, data, len));
/* free the req_ctx, indicating it has fully arrived on the host */ /* free the req_ctx, indicating it has fully arrived on the host */
req_ctx_set_state(rctx, RCTX_S_FREE); usb_buf_free(msg);
} }
/* emulate a TPDU header being sent by the reader/phone */ /* emulate a TPDU header being sent by the reader/phone */
static void rdr_send_tpdu_hdr(struct card_handle *ch, const uint8_t *tpdu_hdr) static void rdr_send_tpdu_hdr(struct card_handle *ch, const uint8_t *tpdu_hdr)
{ {
struct llist_head *queue = usb_get_queue(PHONE_DATAIN);
/* we don't want a receive context to become available during /* we don't want a receive context to become available during
* the first four bytes */ * the first four bytes */
reader_send_bytes(ch, tpdu_hdr, 4); reader_send_bytes(ch, tpdu_hdr, 4);
assert(!req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY)); assert(llist_empty(queue));
reader_send_bytes(ch, tpdu_hdr+4, 1); reader_send_bytes(ch, tpdu_hdr+4, 1);
/* but then after the final byte of the TPDU header, we want a /* but then after the final byte of the TPDU header, we want a
* receive context to be available for USB transmission */ * receive context to be available for USB transmission */
get_and_verify_rctx(RCTX_S_USB_TX_PENDING, tpdu_hdr, 5); get_and_verify_rctx(PHONE_DATAIN, tpdu_hdr, 5);
} }
/* emulate a CEMU_USB_MSGT_DT_TX_DATA received from USB */ /* emulate a SIMTRACE_MSGT_DT_CEMU_TX_DATA received from USB */
static void host_to_device_data(const uint8_t *data, uint16_t len, unsigned int flags) static void host_to_device_data(struct card_handle *ch, const uint8_t *data, uint16_t len,
unsigned int flags)
{ {
struct req_ctx *rctx; struct msgb *msg;
struct simtrace_msg_hdr *mh;
struct cardemu_usb_msg_tx_data *rd; struct cardemu_usb_msg_tx_data *rd;
struct llist_head *queue;
/* allocate a free req_ctx */ /* allocate a free req_ctx */
rctx = req_ctx_find_get(0, RCTX_S_FREE, RCTX_S_USB_RX_BUSY); msg = usb_buf_alloc(PHONE_DATAOUT);
assert(rctx); assert(msg);
/* initialize the common header */
msg->l1h = msg->head;
mh = (struct simtrace_msg_hdr *) msgb_put(msg, sizeof(*mh));
mh->msg_class = SIMTRACE_MSGC_CARDEM;
mh->msg_type = SIMTRACE_MSGT_DT_CEMU_TX_DATA;
/* initialize the header */ /* initialize the tx_data message */
rd = (struct cardemu_usb_msg_tx_data *) rctx->data; msg->l2h = msgb_put(msg, sizeof(*rd) + len);
rctx->tot_len = sizeof(*rd) + len; rd = (struct cardemu_usb_msg_tx_data *) msg->l2h;
cardemu_hdr_set(&rd->hdr, CEMU_USB_MSGT_DT_TX_DATA);
rd->flags = flags; rd->flags = flags;
/* copy data and set length */ /* copy data and set length */
rd->data_len = len; rd->data_len = len;
memcpy(rd->data, data, len); memcpy(rd->data, data, len);
rd->hdr.msg_len = sizeof(*rd) + len;
mh->msg_len = sizeof(*mh) + sizeof(*rd) + len;
/* hand the req_ctx to the UART transmit code */ /* hand the req_ctx to the UART transmit code */
req_ctx_set_state(rctx, RCTX_S_UART_TX_PENDING); queue = card_emu_get_uart_tx_queue(ch);
assert(queue);
msgb_enqueue(queue, msg);
} }
/* card-transmit any pending characters */ /* card-transmit any pending characters */
@@ -270,7 +297,7 @@ test_tpdu_reader2card(struct card_handle *ch, const uint8_t *hdr, const uint8_t
card_tx_verify_chars(ch, NULL, 0); card_tx_verify_chars(ch, NULL, 0);
/* card emulator PC sends a singly byte PB response via USB */ /* card emulator PC sends a singly byte PB response via USB */
host_to_device_data(hdr+1, 1, CEMU_DATA_F_FINAL | CEMU_DATA_F_PB_AND_RX); host_to_device_data(ch, hdr+1, 1, CEMU_DATA_F_FINAL | CEMU_DATA_F_PB_AND_RX);
/* card actually sends that single PB */ /* card actually sends that single PB */
card_tx_verify_chars(ch, hdr+1, 1); card_tx_verify_chars(ch, hdr+1, 1);
@@ -278,13 +305,13 @@ test_tpdu_reader2card(struct card_handle *ch, const uint8_t *hdr, const uint8_t
reader_send_bytes(ch, body, body_len); reader_send_bytes(ch, body, body_len);
/* check if we have received them on the USB side */ /* check if we have received them on the USB side */
get_and_verify_rctx(RCTX_S_USB_TX_PENDING, body, body_len); get_and_verify_rctx(PHONE_DATAIN, body, body_len);
/* ensure there is no extra data received on usb */ /* ensure there is no extra data received on usb */
assert(!req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY)); assert(llist_empty(usb_get_queue(PHONE_DATAOUT)));
/* card emulator sends SW via USB */ /* card emulator sends SW via USB */
host_to_device_data(tpdu_pb_sw, sizeof(tpdu_pb_sw), host_to_device_data(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw),
CEMU_DATA_F_FINAL | CEMU_DATA_F_PB_AND_TX); CEMU_DATA_F_FINAL | CEMU_DATA_F_PB_AND_TX);
/* obtain any pending tx chars */ /* obtain any pending tx chars */
card_tx_verify_chars(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw)); card_tx_verify_chars(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw));
@@ -304,21 +331,21 @@ test_tpdu_card2reader(struct card_handle *ch, const uint8_t *hdr, const uint8_t
card_tx_verify_chars(ch, NULL, 0); card_tx_verify_chars(ch, NULL, 0);
/* card emulator PC sends a response PB via USB */ /* card emulator PC sends a response PB via USB */
host_to_device_data(hdr+1, 1, CEMU_DATA_F_PB_AND_TX); host_to_device_data(ch, hdr+1, 1, CEMU_DATA_F_PB_AND_TX);
/* card actually sends that PB */ /* card actually sends that PB */
card_tx_verify_chars(ch, hdr+1, 1); card_tx_verify_chars(ch, hdr+1, 1);
/* emulate more characters from card to reader */ /* emulate more characters from card to reader */
host_to_device_data(body, body_len, 0); host_to_device_data(ch, body, body_len, 0);
/* obtain those bytes as they arrvive on the card */ /* obtain those bytes as they arrvive on the card */
card_tx_verify_chars(ch, body, body_len); card_tx_verify_chars(ch, body, body_len);
/* ensure there is no extra data received on usb */ /* ensure there is no extra data received on usb */
assert(!req_ctx_find_get(0, RCTX_S_USB_TX_PENDING, RCTX_S_USB_TX_BUSY)); assert(llist_empty(usb_get_queue(PHONE_DATAOUT)));
/* card emulator sends SW via USB */ /* card emulator sends SW via USB */
host_to_device_data(tpdu_pb_sw, sizeof(tpdu_pb_sw), CEMU_DATA_F_FINAL); host_to_device_data(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw), CEMU_DATA_F_FINAL);
/* obtain any pending tx chars */ /* obtain any pending tx chars */
card_tx_verify_chars(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw)); card_tx_verify_chars(ch, tpdu_pb_sw, sizeof(tpdu_pb_sw));
@@ -363,11 +390,11 @@ int main(int argc, char **argv)
struct card_handle *ch; struct card_handle *ch;
unsigned int i; unsigned int i;
req_ctx_init(); ch = card_emu_init(0, 23, 42, PHONE_DATAIN, PHONE_INT);
ch = card_emu_init(0, 23, 42);
assert(ch); assert(ch);
usb_buf_init();
/* start up the card (VCC/RST, ATR) */ /* start up the card (VCC/RST, ATR) */
io_start_card(ch); io_start_card(ch);
card_tx_verify_chars(ch, NULL, 0); card_tx_verify_chars(ch, NULL, 0);

View File

@@ -0,0 +1,184 @@
/* AT91SAM3 USB string descriptor builder
* (C) 2006-2017 by Harald Welte <laforge@gnumonks.org>
*
* 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) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Based on existing utf8_to_utf16le() function,
* Copyright (C) 2003 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#if 0
static int utf8_to_utf16le(const char *s, uint16_t *cp, unsigned len)
{
int count = 0;
uint8_t c;
uint16_t uchar;
/* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4-byte UTF-8 code points,
* which need surrogate pairs. (Unicode 3.1 can use them.)
*/
while (len != 0 && (c = (uint8_t) *s++) != 0) {
if (c & 0x80) {
// 2-byte sequence:
// 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
if ((c & 0xe0) == 0xc0) {
uchar = (c & 0x1f) << 6;
c = (uint8_t) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
// 3-byte sequence (most CJKV characters):
// zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
} else if ((c & 0xf0) == 0xe0) {
uchar = (c & 0x0f) << 12;
c = (uint8_t) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (uint8_t) *s++;
if ((c & 0xc0) != 0xc0)
goto fail;
c &= 0x3f;
uchar |= c;
/* no bogus surrogates */
if (0xd800 <= uchar && uchar <= 0xdfff)
goto fail;
// 4-byte sequence (surrogate pairs, currently rare):
// 11101110wwwwzzzzyy + 110111yyyyxxxxxx
// = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
// (uuuuu = wwww + 1)
// FIXME accept the surrogate code points (only)
} else
goto fail;
} else
uchar = c;
*cp++ = uchar;
count++;
len--;
}
return count;
fail:
return -1;
}
#define COLUMNS 6
static int print_array16(uint16_t *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
int mod = i % COLUMNS;
char *suffix;
char *prefix;
switch (mod) {
case 0:
if (i == 0)
prefix = "\t";
else
prefix= "\t\t\t";
suffix = ", ";
break;
case COLUMNS-1:
prefix = "";
suffix = ",\n";
break;
default:
prefix = "";
suffix = ", ";
break;
}
printf("%s0x%04x%s", prefix, buf[i], suffix);
}
}
#endif
static void print_structhdr(int i, int size)
{
printf("static const unsigned char string%d[] = {\n", i);
printf("\tUSBStringDescriptor_LENGTH(%d),\n", size);
printf("\tUSBGenericDescriptor_STRING,\n");
}
static void print_structftr(void)
{
printf("};\n\n");
}
int main(int argc, char **argv)
{
char asciibuf[512+1];
uint16_t utf16buf[1024+1];
int len;
int j, i = 1;
printf("#pragma once\n\n");
printf("/* THIS FILE IS AUTOGENERATED, DO NOT MODIFY MANUALLY */\n\n");
printf("#include \"usb/include/USBDescriptors.h\"\n\n");
print_structhdr(0, 1);
printf("\tUSBStringDescriptor_ENGLISH_US\n");
print_structftr();
#if 0
printf("static const struct usb_string_descriptor string0 = {\n"
"\t.bLength = sizeof(string0) + 1 * sizeof(uint16_t),\n"
"\t.bDescriptorType = USB_DT_STRING,\n"
"\t.wData[0] = 0x0409, /* English */\n"
"};\n\n");
#endif
while (scanf("%512[^\n]\n", asciibuf) != EOF) {
len = strlen(asciibuf);
printf("/* String %u \"%s\" */\n", i, asciibuf);
print_structhdr(i, len);
for (j = 0; j < len; j++)
printf("\tUSBStringDescriptor_UNICODE('%c'),\n", asciibuf[j]);
print_structftr();
i++;
}
printf("const unsigned char *usb_strings[] = {\n");
for (j = 0; j < i; j++)
printf("\tstring%d,\n", j);
printf("};\n\n");
exit(0);
}

1693
hardware/board_gpio.gnumeric Normal file

File diff suppressed because it is too large Load Diff

21
host/99-simtrace2.rules Normal file
View File

@@ -0,0 +1,21 @@
# udev rules to set the access rights of GemPC smart card readers
# so they can be used by pcscd
# If not adding the device, go away
ACTION!="add", GOTO="simtrace2_rules_end"
SUBSYSTEM!="usb", GOTO="simtrace2_rules_end"
# sysmocom SIMtrace2 (DFU and runtime)
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60e2", GROUP="plugdev"
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60e3", GROUP="plugdev"
# sysmocom OWHW (DFU and runtime)
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="4000", GROUP="plugdev"
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="4001", GROUP="plugdev"
# sysmocom QMOD hub
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="4002", GROUP="plugdev"
# sysmocom QMOD SAM3 (DFU and runtime)
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="4003", GROUP="plugdev"
ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="4004", GROUP="plugdev"
# All done
LABEL="simtrace2_rules_end"

View File

@@ -1,15 +1,19 @@
LDFLAGS=`pkg-config --libs libusb-1.0 libosmocore` -losmocore LDFLAGS=`pkg-config --libs libusb-1.0 libosmocore` -losmocore
CFLAGS=-Wall -g
all: simtrace2-remsim simtrace2-remsim-usb2udp all: simtrace2-remsim simtrace2-remsim-usb2udp simtrace2-list
simtrace2-remsim: simtrace2-remsim.o apdu_dispatch.o simtrace2-discovery.o simtrace2-remsim: simtrace2-remsim.o apdu_dispatch.o simtrace2-discovery.o libusb_util.o
$(CC) -o $@ $^ $(LDFLAGS) -losmosim $(CC) -o $@ $^ $(LDFLAGS) -losmosim
simtrace2-remsim-usb2udp: usb2udp.o simtrace2-discovery.o simtrace2-remsim-usb2udp: usb2udp.o simtrace2-discovery.o
$(CC) -o $@ $^ $(LDFLAGS) $(CC) -o $@ $^ $(LDFLAGS)
simtrace2-list: simtrace2_usb.o libusb_util.o
$(CC) -o $@ $^ $(LDFLAGS)
%.o: %.c %.o: %.c
$(CC) $(CFLAGS) `pkg-config --cflags libusb-1.0 libosmocore` -o $@ -c $^ $(CC) $(CFLAGS) `pkg-config --cflags libusb-1.0 libosmocore` -o $@ -c $^
clean: clean:
@rm -f simtrace2-remsim simtrace2-remsim-usb2udp *.o @rm -f simtrace2-remsim simtrace2-remsim-usb2udp simtrace2-list *.o

View File

@@ -1 +0,0 @@
../firmware/src_simtrace/cardemu_prot.h

274
host/libusb_util.c Normal file
View File

@@ -0,0 +1,274 @@
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <libusb.h>
#include "libusb_util.h"
static char path_buf[USB_MAX_PATH_LEN];
static char *get_path(libusb_device *dev)
{
#if (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102) || (defined(LIBUSBX_API_VERSION) && LIBUSBX_API_VERSION >= 0x01000102)
uint8_t path[8];
int r,j;
r = libusb_get_port_numbers(dev, path, sizeof(path));
if (r > 0) {
sprintf(path_buf,"%d-%d",libusb_get_bus_number(dev),path[0]);
for (j = 1; j < r; j++){
sprintf(path_buf+strlen(path_buf),".%d",path[j]);
};
}
return path_buf;
#else
# warning "libusb too old - building without USB path support!"
return NULL;
#endif
}
static int match_dev_id(const struct libusb_device_descriptor *desc, const struct dev_id *id)
{
if ((desc->idVendor == id->vendor_id) && (desc->idProduct == id->product_id))
return 1;
return 0;
}
static int match_dev_ids(const struct libusb_device_descriptor *desc, const struct dev_id *ids)
{
const struct dev_id *id;
for (id = ids; id->vendor_id || id->product_id; id++) {
if (match_dev_id(desc, id))
return 1;
}
return 0;
}
libusb_device **find_matching_usb_devs(const struct dev_id *dev_ids)
{
libusb_device **list;
libusb_device **out = calloc(256, sizeof(libusb_device *));
libusb_device **cur = out;
unsigned int i;
int rc;
if (!out)
return NULL;
rc = libusb_get_device_list(NULL, &list);
if (rc <= 0) {
perror("No USB devices found");
free(out);
return NULL;
}
for (i = 0; list[i] != NULL; i++) {
struct libusb_device_descriptor dev_desc;
libusb_device *dev = list[i];
rc = libusb_get_device_descriptor(dev, &dev_desc);
if (rc < 0) {
perror("Couldn't get device descriptor\n");
libusb_unref_device(dev);
continue;
}
if (match_dev_ids(&dev_desc, dev_ids)) {
*cur = dev;
cur++;
/* FIXME: overflow check */
} else
libusb_unref_device(dev);
}
if (cur == out) {
libusb_free_device_list(list, 1);
free(out);
return NULL;
}
libusb_free_device_list(list, 0);
return out;
}
int dev_find_matching_interfaces(libusb_device *dev, int class, int sub_class, int protocol,
struct usb_interface_match *out, unsigned int out_len)
{
struct libusb_device_descriptor dev_desc;
int rc, i, out_idx = 0;
uint8_t addr;
char *path;
rc = libusb_get_device_descriptor(dev, &dev_desc);
if (rc < 0) {
perror("Couldn't get device descriptor\n");
return -EIO;
}
addr = libusb_get_device_address(dev);
path = get_path(dev);
/* iterate over all configurations */
for (i = 0; i < dev_desc.bNumConfigurations; i++) {
struct libusb_config_descriptor *conf_desc;
int j;
rc = libusb_get_config_descriptor(dev, i, &conf_desc);
if (rc < 0) {
fprintf(stderr, "Couldn't get config descriptor %u\n", i);
continue;
}
/* iterate over all interfaces */
for (j = 0; j < conf_desc->bNumInterfaces; j++) {
const struct libusb_interface *intf = &conf_desc->interface[j];
int k;
/* iterate over all alternate settings */
for (k = 0; k < intf->num_altsetting; k++) {
const struct libusb_interface_descriptor *if_desc;
if_desc = &intf->altsetting[k];
if (class > 0 && if_desc->bInterfaceClass != class)
continue;
if (sub_class > 0 && if_desc->bInterfaceSubClass != sub_class)
continue;
if (protocol > 0 && if_desc->bInterfaceProtocol != protocol)
continue;
/* MATCH! */
out[out_idx].usb_dev = dev;
out[out_idx].vendor = dev_desc.idVendor;
out[out_idx].product = dev_desc.idProduct;
out[out_idx].addr = addr;
strncpy(out[out_idx].path, path, sizeof(out[out_idx].path));
out[out_idx].path[sizeof(out[out_idx].path)-1] = '\0';
out[out_idx].configuration = conf_desc->bConfigurationValue;
out[out_idx].interface = if_desc->bInterfaceNumber;
out[out_idx].altsetting = if_desc->bAlternateSetting;
out[out_idx].class = if_desc->bInterfaceClass;
out[out_idx].sub_class = if_desc->bInterfaceSubClass;
out[out_idx].protocol = if_desc->bInterfaceProtocol;
out[out_idx].string_idx = if_desc->iInterface;
out_idx++;
if (out_idx >= out_len)
return out_idx;
}
}
}
return out_idx;
}
int usb_match_interfaces(libusb_context *ctx, const struct dev_id *dev_ids,
int class, int sub_class, int protocol,
struct usb_interface_match *out, unsigned int out_len)
{
struct usb_interface_match *out_cur = out;
unsigned int out_len_remain = out_len;
libusb_device **list;
libusb_device **dev;
list = find_matching_usb_devs(dev_ids);
if (!list)
return 0;
for (dev = list; *dev; dev++) {
int rc;
#if 0
struct libusb_device_descriptor dev_desc;
uint8_t ports[8];
uint8_t addr;
rc = libusb_get_device_descriptor(*dev, &dev_desc);
if (rc < 0) {
perror("Cannot get device descriptor");
continue;
}
addr = libusb_get_device_address(*dev);
rc = libusb_get_port_numbers(*dev, ports, sizeof(ports));
if (rc < 0) {
perror("Cannot get device path");
continue;
}
printf("Found USB Device %04x:%04x at address %d\n",
dev_desc.idVendor, dev_desc.idProduct, addr);
#endif
rc = dev_find_matching_interfaces(*dev, 255, 2, -1, out_cur, out_len_remain);
if (rc < 0)
continue;
out_cur += rc;
out_len_remain -= rc;
}
return out_len - out_len_remain;
}
libusb_device_handle *usb_open_claim_interface(libusb_context *ctx,
const struct usb_interface_match *ifm)
{
int rc, config;
struct dev_id dev_ids[] = { { ifm->vendor, ifm->product }, { 0, 0 } };
libusb_device **list;
libusb_device **dev;
libusb_device_handle *usb_devh = NULL;
list = find_matching_usb_devs(dev_ids);
if (!list) {
perror("No USB device with matching VID/PID");
return NULL;
}
for (dev = list; *dev; dev++) {
int addr;
char *path;
addr = libusb_get_device_address(*dev);
path = get_path(*dev);
if ((ifm->addr && addr == ifm->addr) ||
(strlen(ifm->path) && !strcmp(path, ifm->path))) {
rc = libusb_open(*dev, &usb_devh);
if (rc < 0) {
perror("Cannot open device");
break;
}
rc = libusb_get_configuration(usb_devh, &config);
if (rc < 0) {
perror("Cannot get current configuration");
libusb_close(usb_devh);
break;
}
if (config != ifm->configuration) {
rc = libusb_set_configuration(usb_devh, ifm->configuration);
if (rc < 0) {
perror("Cannot set configuration");
libusb_close(usb_devh);
break;
}
}
rc = libusb_claim_interface(usb_devh, ifm->interface);
if (rc < 0) {
perror("Cannot claim interface");
libusb_close(usb_devh);
break;
}
rc = libusb_set_interface_alt_setting(usb_devh, ifm->interface, ifm->altsetting);
if (rc < 0) {
perror("Cannot set interface altsetting");
libusb_release_interface(usb_devh, ifm->interface);
libusb_close(usb_devh);
break;
}
}
}
/* unref / free list */
for (dev = list; *dev; dev++)
libusb_unref_device(*dev);
free(list);
return usb_devh;
}

52
host/libusb_util.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <libusb.h>
#define USB_MAX_PATH_LEN 20
struct dev_id {
uint16_t vendor_id;
uint16_t product_id;
};
/* Find any USB devices in the system matching the given Vendor and
* Product ID */
libusb_device **find_matching_usb_devs(const struct dev_id *dev_ids);
/* structure describing a single matching interface found */
struct usb_interface_match {
/* libusb device E*/
libusb_device *usb_dev;
/* Vendor ID of the device running matching interface */
uint16_t vendor;
/* Product ID of the device running matching interface */
uint16_t product;
/* USB Bus Address */
uint8_t addr;
/* physical path */
char path[USB_MAX_PATH_LEN];
/* configuration of matching interface */
uint8_t configuration;
/* interface number of matching interface */
uint8_t interface;
/* altsetting of matching interface */
uint8_t altsetting;
/* bInterfaceClass of matching interface */
uint8_t class;
/* bInterfaceSubClass of matching interface */
uint8_t sub_class;
/* bInterfaceProtocol of matching interface */
uint8_t protocol;
/* index of string descriptor of matching interface */
uint8_t string_idx;
};
int dev_find_matching_interfaces(libusb_device *dev, int class, int sub_class, int protocol,
struct usb_interface_match *out, unsigned int out_len);
int usb_match_interfaces(libusb_context *ctx, const struct dev_id *dev_ids,
int class, int sub_class, int protocol,
struct usb_interface_match *out, unsigned int out_len);
libusb_device_handle *usb_open_claim_interface(libusb_context *ctx,
const struct usb_interface_match *ifm);

View File

@@ -1,6 +1,6 @@
/* simtrace2-remsim - main program for the host PC /* simtrace2-remsim - main program for the host PC
* *
* (C) 2010-2016 by Harald Welte <hwelte@hmw-consulting.de> * (C) 2010-2017 by Harald Welte <hwelte@hmw-consulting.de>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 * it under the terms of the GNU General Public License version 2
@@ -35,8 +35,9 @@
#include <libusb.h> #include <libusb.h>
#include "libusb_util.h"
#include "simtrace.h" #include "simtrace.h"
#include "cardemu_prot.h" #include "simtrace_prot.h"
#include "apdu_dispatch.h" #include "apdu_dispatch.h"
#include "simtrace2-discovery.h" #include "simtrace2-discovery.h"
@@ -44,16 +45,44 @@
#include <osmocom/core/gsmtap_util.h> #include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h> #include <osmocom/core/utils.h>
#include <osmocom/core/socket.h> #include <osmocom/core/socket.h>
#include <osmocom/core/msgb.h>
#include <osmocom/sim/class_tables.h> #include <osmocom/sim/class_tables.h>
#include <osmocom/sim/sim.h> #include <osmocom/sim/sim.h>
/* transport to a SIMtrace device */
struct st_transport {
/* USB */
struct libusb_device_handle *usb_devh;
struct {
uint8_t in;
uint8_t out;
uint8_t irq_in;
} usb_ep;
/* UDP */
int udp_fd;
};
/* a SIMtrace slot; communicates over a transport */
struct st_slot {
/* transport through which the slot can be reached */
struct st_transport *transp;
/* number of the slot within the transport */
uint8_t slot_nr;
};
/* One istance of card emulation */
struct cardem_inst {
/* slot on which this card emulation instance runs */
struct st_slot *slot;
/* libosmosim SIM card profile */
const struct osim_cla_ins_card_profile *card_prof;
/* libosmosim SIM card channel */
struct osim_chan_hdl *chan;
};
/* global GSMTAP instance */
static struct gsmtap_inst *g_gti; static struct gsmtap_inst *g_gti;
struct libusb_device_handle *g_devh;
const struct osim_cla_ins_card_profile *g_prof;
static uint8_t g_in_ep;
static uint8_t g_out_ep;
static int g_udp_fd = -1;
static struct osim_chan_hdl *g_chan;
static int gsmtap_send_sim(const uint8_t *apdu, unsigned int len) static int gsmtap_send_sim(const uint8_t *apdu, unsigned int len)
{ {
@@ -84,6 +113,16 @@ static int gsmtap_send_sim(const uint8_t *apdu, unsigned int len)
return 0; return 0;
} }
/***********************************************************************
* SIMTRACE pcore protocol
***********************************************************************/
/*! \brief allocate a message buffer for simtrace use */
static struct msgb *st_msgb_alloc(void)
{
return msgb_alloc_headroom(1024+32, 32, "SIMtrace");
}
#if 0 #if 0
static void apdu_out_cb(uint8_t *buf, unsigned int len, void *user_data) static void apdu_out_cb(uint8_t *buf, unsigned int len, void *user_data)
{ {
@@ -93,91 +132,137 @@ static void apdu_out_cb(uint8_t *buf, unsigned int len, void *user_data)
#endif #endif
/*! \brief Transmit a given command to the SIMtrace2 device */ /*! \brief Transmit a given command to the SIMtrace2 device */
static int tx_to_dev(uint8_t *buf, unsigned int len) int st_transp_tx_msg(struct st_transport *transp, struct msgb *msg)
{ {
struct cardemu_usb_msg_hdr *mh = (struct cardemu_usb_msg_hdr *) buf; int rc;
int xfer_len;
mh->msg_len = len; printf("<- %s\n", msgb_hexdump(msg));
printf("<- %s\n", osmo_hexdump(buf, len)); if (transp->udp_fd < 0) {
int xfer_len;
if (g_udp_fd < 0) { rc = libusb_bulk_transfer(transp->usb_devh, transp->usb_ep.out,
return libusb_bulk_transfer(g_devh, g_out_ep, buf, len, msgb_data(msg), msgb_length(msg),
&xfer_len, 100000); &xfer_len, 100000);
} else { } else {
return write(g_udp_fd, buf, len); rc = write(transp->udp_fd, msgb_data(msg), msgb_length(msg));
} }
msgb_free(msg);
return rc;
} }
/*! \brief Request the SIMtrace2 to generate a card-insert signal */ static struct simtrace_msg_hdr *st_push_hdr(struct msgb *msg, uint8_t msg_class, uint8_t msg_type,
static int request_card_insert(bool inserted) uint8_t slot_nr)
{ {
struct cardemu_usb_msg_cardinsert cins; struct simtrace_msg_hdr *sh;
memset(&cins, 0, sizeof(cins)); sh = (struct simtrace_msg_hdr *) msgb_push(msg, sizeof(*sh));
cins.hdr.msg_type = CEMU_USB_MSGT_DT_CARDINSERT; memset(sh, 0, sizeof(*sh));
sh->msg_class = msg_class;
sh->msg_type = msg_type;
sh->slot_nr = slot_nr;
sh->msg_len = msgb_length(msg);
return sh;
}
/* transmit a given message to a specified slot. Expects all headers
* present before calling the function */
int st_slot_tx_msg(struct st_slot *slot, struct msgb *msg,
uint8_t msg_class, uint8_t msg_type)
{
struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *) msg->data;
sh->slot_nr = slot->slot_nr;
st_push_hdr(msg, msg_class, msg_type, slot->slot_nr);
return st_transp_tx_msg(slot->transp, msg);
}
/***********************************************************************
* Card Emulation protocol
***********************************************************************/
/*! \brief Request the SIMtrace2 to generate a card-insert signal */
static int cardem_request_card_insert(struct cardem_inst *ci, bool inserted)
{
struct msgb *msg = st_msgb_alloc();
struct cardemu_usb_msg_cardinsert *cins;
cins = (struct cardemu_usb_msg_cardinsert *) msgb_put(msg, sizeof(*cins));
memset(cins, 0, sizeof(*cins));
if (inserted) if (inserted)
cins.card_insert = 1; cins->card_insert = 1;
return tx_to_dev((uint8_t *)&cins, sizeof(cins)); return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_CARDINSERT);
} }
/*! \brief Request the SIMtrace2 to transmit a Procedure Byte, then Rx */ /*! \brief Request the SIMtrace2 to transmit a Procedure Byte, then Rx */
static int request_pb_and_rx(uint8_t pb, uint8_t le) static int cardem_request_pb_and_rx(struct cardem_inst *ci, uint8_t pb, uint8_t le)
{ {
struct msgb *msg = st_msgb_alloc();
struct cardemu_usb_msg_tx_data *txd; struct cardemu_usb_msg_tx_data *txd;
uint8_t buf[sizeof(*txd) + 1]; txd = (struct cardemu_usb_msg_tx_data *) msgb_put(msg, sizeof(*txd));
txd = (struct cardemu_usb_msg_tx_data *) buf;
printf("<= request_pb_and_rx(%02x, %d)\n", pb, le); printf("<= %s(%02x, %d)\n", __func__, pb, le);
memset(txd, 0, sizeof(*txd)); memset(txd, 0, sizeof(*txd));
txd->data_len = 1; txd->data_len = 1;
txd->hdr.msg_type = CEMU_USB_MSGT_DT_TX_DATA;
txd->flags = CEMU_DATA_F_PB_AND_RX; txd->flags = CEMU_DATA_F_PB_AND_RX;
txd->data[0] = pb; /* one data byte */
msgb_put_u8(msg, pb);
return tx_to_dev((uint8_t *)txd, sizeof(*txd)+txd->data_len); return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_TX_DATA);
} }
/*! \brief Request the SIMtrace2 to transmit a Procedure Byte, then Tx */ /*! \brief Request the SIMtrace2 to transmit a Procedure Byte, then Tx */
static int request_pb_and_tx(uint8_t pb, const uint8_t *data, uint8_t data_len_in) static int cardem_request_pb_and_tx(struct cardem_inst *ci, uint8_t pb,
const uint8_t *data, uint8_t data_len_in)
{ {
uint32_t data_len = data_len_in; struct msgb *msg = st_msgb_alloc();
struct cardemu_usb_msg_tx_data *txd; struct cardemu_usb_msg_tx_data *txd;
uint8_t buf[sizeof(*txd) + 1 + data_len_in]; uint8_t *cur;
txd = (struct cardemu_usb_msg_tx_data *) buf;
printf("<= request_pb_and_tx(%02x, %s, %d)\n", pb, osmo_hexdump(data, data_len_in), data_len_in); txd = (struct cardemu_usb_msg_tx_data *) msgb_put(msg, sizeof(*txd));
printf("<= %s(%02x, %s, %d)\n", __func__, pb,
osmo_hexdump(data, data_len_in), data_len_in);
memset(txd, 0, sizeof(*txd)); memset(txd, 0, sizeof(*txd));
txd->hdr.msg_type = CEMU_USB_MSGT_DT_TX_DATA;
txd->data_len = 1 + data_len_in; txd->data_len = 1 + data_len_in;
txd->flags = CEMU_DATA_F_PB_AND_TX; txd->flags = CEMU_DATA_F_PB_AND_TX;
txd->data[0] = pb; /* procedure byte */
memcpy(txd->data+1, data, data_len_in); msgb_put_u8(msg, pb);
/* data */
cur = msgb_put(msg, data_len_in);
memcpy(cur, data, data_len_in);
return tx_to_dev(buf, sizeof(*txd)+txd->data_len); return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_TX_DATA);
} }
/*! \brief Request the SIMtrace2 to send a Status Word */ /*! \brief Request the SIMtrace2 to send a Status Word */
static int request_sw_tx(const uint8_t *sw) static int cardem_request_sw_tx(struct cardem_inst *ci, const uint8_t *sw)
{ {
struct msgb *msg = st_msgb_alloc();
struct cardemu_usb_msg_tx_data *txd; struct cardemu_usb_msg_tx_data *txd;
uint8_t buf[sizeof(*txd) + 2]; uint8_t *cur;
txd = (struct cardemu_usb_msg_tx_data *) buf;
printf("<= request_sw_tx(%02x %02x)\n", sw[0], sw[1]); txd = (struct cardemu_usb_msg_tx_data *) msgb_put(msg, sizeof(*txd));
printf("<= %s(%02x %02x)\n", __func__, sw[0], sw[1]);
memset(txd, 0, sizeof(*txd)); memset(txd, 0, sizeof(*txd));
txd->hdr.msg_type = CEMU_USB_MSGT_DT_TX_DATA;
txd->data_len = 2; txd->data_len = 2;
txd->flags = CEMU_DATA_F_PB_AND_TX | CEMU_DATA_F_FINAL; txd->flags = CEMU_DATA_F_PB_AND_TX | CEMU_DATA_F_FINAL;
txd->data[0] = sw[0]; cur = msgb_put(msg, 2);
txd->data[1] = sw[1]; cur[0] = sw[0];
cur[1] = sw[1];
return tx_to_dev((uint8_t *)txd, sizeof(*txd)+txd->data_len); return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_TX_DATA);
} }
static void atr_update_csum(uint8_t *atr, unsigned int atr_len) static void atr_update_csum(uint8_t *atr, unsigned int atr_len)
@@ -191,24 +276,96 @@ static void atr_update_csum(uint8_t *atr, unsigned int atr_len)
atr[atr_len-1] = csum; atr[atr_len-1] = csum;
} }
static int request_set_atr(const uint8_t *atr, unsigned int atr_len) static int cardem_request_set_atr(struct cardem_inst *ci, const uint8_t *atr, unsigned int atr_len)
{ {
struct msgb *msg = st_msgb_alloc();
struct cardemu_usb_msg_set_atr *satr; struct cardemu_usb_msg_set_atr *satr;
uint8_t buf[sizeof(*satr) + atr_len]; uint8_t *cur;
satr = (struct cardemu_usb_msg_set_atr *) buf;
printf("<= request_set_atr(%s)\n", osmo_hexdump(atr, atr_len)); satr = (struct cardemu_usb_msg_set_atr *) msgb_put(msg, sizeof(*satr));
printf("<= %s(%s)\n", __func__, osmo_hexdump(atr, atr_len));
memset(satr, 0, sizeof(*satr)); memset(satr, 0, sizeof(*satr));
satr->hdr.msg_type = CEMU_USB_MSGT_DT_SET_ATR;
satr->atr_len = atr_len; satr->atr_len = atr_len;
memcpy(satr->atr, atr, atr_len); cur = msgb_put(msg, atr_len);
memcpy(cur, atr, atr_len);
return tx_to_dev((uint8_t *)satr, sizeof(buf)); return st_slot_tx_msg(ci->slot, msg, SIMTRACE_MSGC_CARDEM, SIMTRACE_MSGT_DT_CEMU_SET_ATR);
} }
/***********************************************************************
* Modem Control protocol
***********************************************************************/
static int _modem_reset(struct st_slot *slot, uint8_t asserted, uint16_t pulse_ms)
{
struct msgb *msg = st_msgb_alloc();
struct st_modem_reset *sr ;
sr = (struct st_modem_reset *) msgb_put(msg, sizeof(*sr));
sr->asserted = asserted;
sr->pulse_duration_msec = pulse_ms;
return st_slot_tx_msg(slot, msg, SIMTRACE_MSGC_MODEM, SIMTRACE_MSGT_DT_MODEM_RESET);
}
/*! \brief pulse the RESET line of the modem for \a duration_ms milli-seconds*/
int st_modem_reset_pulse(struct st_slot *slot, uint16_t duration_ms)
{
return _modem_reset(slot, 2, duration_ms);
}
/*! \brief assert the RESET line of the modem */
int st_modem_reset_active(struct st_slot *slot)
{
return _modem_reset(slot, 1, 0);
}
/*! \brief de-assert the RESET line of the modem */
int st_modem_reset_inactive(struct st_slot *slot)
{
return _modem_reset(slot, 0, 0);
}
static int _modem_sim_select(struct st_slot *slot, uint8_t remote_sim)
{
struct msgb *msg = st_msgb_alloc();
struct st_modem_sim_select *ss;
ss = (struct st_modem_sim_select *) msgb_put(msg, sizeof(*ss));
ss->remote_sim = remote_sim;
return st_slot_tx_msg(slot, msg, SIMTRACE_MSGC_MODEM, SIMTRACE_MSGT_DT_MODEM_SIM_SELECT);
}
/*! \brief select local (physical) SIM for given slot */
int st_modem_sim_select_local(struct st_slot *slot)
{
return _modem_sim_select(slot, 0);
}
/*! \brief select remote (emulated/forwarded) SIM for given slot */
int st_modem_sim_select_remote(struct st_slot *slot)
{
return _modem_sim_select(slot, 1);
}
/*! \brief Request slot to send us status information about the modem */
int st_modem_get_status(struct st_slot *slot)
{
struct msgb *msg = st_msgb_alloc();
return st_slot_tx_msg(slot, msg, SIMTRACE_MSGC_MODEM, SIMTRACE_MSGT_BD_MODEM_STATUS);
}
/***********************************************************************
* Incoming Messages
***********************************************************************/
/*! \brief Process a STATUS message from the SIMtrace2 */ /*! \brief Process a STATUS message from the SIMtrace2 */
static int process_do_status(uint8_t *buf, int len) static int process_do_status(struct cardem_inst *ci, uint8_t *buf, int len)
{ {
struct cardemu_usb_msg_status *status; struct cardemu_usb_msg_status *status;
status = (struct cardemu_usb_msg_status *) buf; status = (struct cardemu_usb_msg_status *) buf;
@@ -221,7 +378,7 @@ static int process_do_status(uint8_t *buf, int len)
} }
/*! \brief Process a PTS indication message from the SIMtrace2 */ /*! \brief Process a PTS indication message from the SIMtrace2 */
static int process_do_pts(uint8_t *buf, int len) static int process_do_pts(struct cardem_inst *ci, uint8_t *buf, int len)
{ {
struct cardemu_usb_msg_pts_info *pts; struct cardemu_usb_msg_pts_info *pts;
pts = (struct cardemu_usb_msg_pts_info *) buf; pts = (struct cardemu_usb_msg_pts_info *) buf;
@@ -232,24 +389,23 @@ static int process_do_pts(uint8_t *buf, int len)
} }
/*! \brief Process a ERROR indication message from the SIMtrace2 */ /*! \brief Process a ERROR indication message from the SIMtrace2 */
static int process_do_error(uint8_t *buf, int len) static int process_do_error(struct cardem_inst *ci, uint8_t *buf, int len)
{ {
struct cardemu_usb_msg_error *err; struct cardemu_usb_msg_error *err;
err = (struct cardemu_usb_msg_error *) buf; err = (struct cardemu_usb_msg_error *) buf;
printf("=> ERROR: %u/%u/%u: %s\n", printf("=> ERROR: %u/%u/%u: %s\n",
err->severity, err->subsystem, err->code, err->severity, err->subsystem, err->code,
err->msg_len ? err->msg : ""); err->msg_len ? (char *)err->msg : "");
return 0; return 0;
} }
/*! \brief Process a RX-DATA indication message from the SIMtrace2 */ /*! \brief Process a RX-DATA indication message from the SIMtrace2 */
static int process_do_rx_da(uint8_t *buf, int len) static int process_do_rx_da(struct cardem_inst *ci, uint8_t *buf, int len)
{ {
static struct apdu_context ac; static struct apdu_context ac;
struct cardemu_usb_msg_rx_data *data; struct cardemu_usb_msg_rx_data *data;
const uint8_t sw_success[] = { 0x90, 0x00 };
int rc; int rc;
data = (struct cardemu_usb_msg_rx_data *) buf; data = (struct cardemu_usb_msg_rx_data *) buf;
@@ -262,7 +418,7 @@ static int process_do_rx_da(uint8_t *buf, int len)
if (rc & APDU_ACT_TX_CAPDU_TO_CARD) { if (rc & APDU_ACT_TX_CAPDU_TO_CARD) {
struct msgb *tmsg = msgb_alloc(1024, "TPDU"); struct msgb *tmsg = msgb_alloc(1024, "TPDU");
struct osim_reader_hdl *rh = g_chan->card->reader; struct osim_reader_hdl *rh = ci->chan->card->reader;
uint8_t *cur; uint8_t *cur;
/* Copy TPDU header */ /* Copy TPDU header */
@@ -286,36 +442,39 @@ static int process_do_rx_da(uint8_t *buf, int len)
ac.sw[1] = msgb_apdu_sw(tmsg) & 0xff; ac.sw[1] = msgb_apdu_sw(tmsg) & 0xff;
printf("SW=0x%04x, len_rx=%d\n", msgb_apdu_sw(tmsg), msgb_l3len(tmsg)); printf("SW=0x%04x, len_rx=%d\n", msgb_apdu_sw(tmsg), msgb_l3len(tmsg));
if (msgb_l3len(tmsg)) if (msgb_l3len(tmsg))
request_pb_and_tx(ac.hdr.ins, tmsg->l3h, msgb_l3len(tmsg)); cardem_request_pb_and_tx(ci, ac.hdr.ins, tmsg->l3h, msgb_l3len(tmsg));
request_sw_tx(ac.sw); cardem_request_sw_tx(ci, ac.sw);
} else if (ac.lc.tot > ac.lc.cur) { } else if (ac.lc.tot > ac.lc.cur) {
request_pb_and_rx(ac.hdr.ins, ac.lc.tot - ac.lc.cur); cardem_request_pb_and_rx(ci, ac.hdr.ins, ac.lc.tot - ac.lc.cur);
} }
return 0; return 0;
} }
#if 0
case SIMTRACE_CMD_DO_ERROR
rc = process_do_error(ci, buf, len);
break;
#endif
/*! \brief Process an incoming message from the SIMtrace2 */ /*! \brief Process an incoming message from the SIMtrace2 */
static int process_usb_msg(uint8_t *buf, int len) static int process_usb_msg(struct cardem_inst *ci, uint8_t *buf, int len)
{ {
struct cardemu_usb_msg_hdr *sh = (struct cardemu_usb_msg_hdr *)buf; struct simtrace_msg_hdr *sh = (struct simtrace_msg_hdr *)buf;
uint8_t *payload;
int payload_len;
int rc; int rc;
printf("-> %s\n", osmo_hexdump(buf, len)); printf("-> %s\n", osmo_hexdump(buf, len));
buf += sizeof(*sh);
switch (sh->msg_type) { switch (sh->msg_type) {
case CEMU_USB_MSGT_DO_STATUS: case SIMTRACE_MSGT_BD_CEMU_STATUS:
rc = process_do_status(buf, len); rc = process_do_status(ci, buf, len);
break; break;
case CEMU_USB_MSGT_DO_PTS: case SIMTRACE_MSGT_DO_CEMU_PTS:
rc = process_do_pts(buf, len); rc = process_do_pts(ci, buf, len);
break; break;
case CEMU_USB_MSGT_DO_ERROR: case SIMTRACE_MSGT_DO_CEMU_RX_DATA:
rc = process_do_error(buf, len); rc = process_do_rx_da(ci, buf, len);
break;
case CEMU_USB_MSGT_DO_RX_DATA:
rc = process_do_rx_da(buf, len);
break; break;
default: default:
printf("unknown simtrace msg type 0x%02x\n", sh->msg_type); printf("unknown simtrace msg type 0x%02x\n", sh->msg_type);
@@ -329,31 +488,49 @@ static int process_usb_msg(uint8_t *buf, int len)
static void print_welcome(void) static void print_welcome(void)
{ {
printf("simtrace2-remsim - Remote SIM card forwarding\n" printf("simtrace2-remsim - Remote SIM card forwarding\n"
"(C) 2010-2016 by Harald Welte <laforge@gnumonks.org>\n\n"); "(C) 2010-2017 by Harald Welte <laforge@gnumonks.org>\n\n");
} }
static void print_help(void) static void print_help(void)
{ {
printf( "\t-i\t--gsmtap-ip\tA.B.C.D\n" printf( "\t-r\t--remote-udp-host HOST\n"
"\t-a\t--skip-atr\n" "\t-p\t--remote-udp-port PORT\n"
"\t-h\t--help\n" "\t-h\t--help\n"
"\t-i\t--gsmtap-ip\tA.B.C.D\n"
"\t-a\t--skip-atr\n"
"\t-k\t--keep-running\n" "\t-k\t--keep-running\n"
"\t-V\t--usb-vendor\tVENDOR_ID\n"
"\t-P\t--usb-product\tPRODUCT_ID\n"
"\t-C\t--usb-config\tCONFIG_ID\n"
"\t-I\t--usb-interface\tINTERFACE_ID\n"
"\t-S\t--usb-altsetting ALTSETTING_ID\n"
"\t-A\t--usb-address\tADDRESS\n"
"\n" "\n"
); );
} }
static const struct option opts[] = { static const struct option opts[] = {
{ "remote-udp-host", 1, 0, 'r' },
{ "remote-udp-port", 1, 0, 'p' },
{ "gsmtap-ip", 1, 0, 'i' }, { "gsmtap-ip", 1, 0, 'i' },
{ "skip-atr", 0, 0, 'a' }, { "skip-atr", 0, 0, 'a' },
{ "help", 0, 0, 'h' }, { "help", 0, 0, 'h' },
{ "keep-running", 0, 0, 'k' }, { "keep-running", 0, 0, 'k' },
{ "usb-vendor", 1, 0, 'V' },
{ "usb-product", 1, 0, 'P' },
{ "usb-config", 1, 0, 'C' },
{ "usb-interface", 1, 0, 'I' },
{ "usb-altsetting", 1, 0, 'S' },
{ "usb-address", 1, 0, 'A' },
{ "usb-path", 1, 0, 'H' },
{ NULL, 0, 0, 0 } { NULL, 0, 0, 0 }
}; };
static void run_mainloop(void) static void run_mainloop(struct cardem_inst *ci)
{ {
struct st_transport *transp = ci->slot->transp;
unsigned int msg_count, byte_count = 0; unsigned int msg_count, byte_count = 0;
char buf[16*265]; uint8_t buf[16*265];
int xfer_len; int xfer_len;
int rc; int rc;
@@ -361,14 +538,17 @@ static void run_mainloop(void)
while (1) { while (1) {
/* read data from SIMtrace2 device (local or via USB) */ /* read data from SIMtrace2 device (local or via USB) */
if (g_udp_fd < 0) { if (transp->udp_fd < 0) {
rc = libusb_bulk_transfer(g_devh, g_in_ep, buf, sizeof(buf), &xfer_len, 100000); rc = libusb_bulk_transfer(transp->usb_devh, transp->usb_ep.in,
if (rc < 0 && rc != LIBUSB_ERROR_TIMEOUT) { buf, sizeof(buf), &xfer_len, 100000);
if (rc < 0 && rc != LIBUSB_ERROR_TIMEOUT &&
rc != LIBUSB_ERROR_INTERRUPTED &&
rc != LIBUSB_ERROR_IO) {
fprintf(stderr, "BULK IN transfer error; rc=%d\n", rc); fprintf(stderr, "BULK IN transfer error; rc=%d\n", rc);
return; return;
} }
} else { } else {
rc = read(g_udp_fd, buf, sizeof(buf)); rc = read(transp->udp_fd, buf, sizeof(buf));
if (rc <= 0) { if (rc <= 0) {
fprintf(stderr, "shor read from UDP\n"); fprintf(stderr, "shor read from UDP\n");
return; return;
@@ -377,19 +557,32 @@ static void run_mainloop(void)
} }
/* dispatch any incoming data */ /* dispatch any incoming data */
if (xfer_len > 0) { if (xfer_len > 0) {
//printf("URB: %s\n", osmo_hexdump(buf, rc)); printf("URB: %s\n", osmo_hexdump(buf, rc));
process_usb_msg(buf, xfer_len); process_usb_msg(ci, buf, xfer_len);
msg_count++; msg_count++;
byte_count += xfer_len; byte_count += xfer_len;
} }
} }
} }
static struct st_transport _transp;
static struct st_slot _slot = {
.transp = &_transp,
.slot_nr = 0,
};
struct cardem_inst _ci = {
.slot = &_slot,
};
struct cardem_inst *ci = &_ci;
static void signal_handler(int signal) static void signal_handler(int signal)
{ {
switch (signal) { switch (signal) {
case SIGINT: case SIGINT:
request_card_insert(false); cardem_request_card_insert(ci, false);
exit(0); exit(0);
break; break;
default: default:
@@ -399,14 +592,17 @@ static void signal_handler(int signal)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
struct st_transport *transp = ci->slot->transp;
char *gsmtap_host = "127.0.0.1"; char *gsmtap_host = "127.0.0.1";
int rc; int rc;
int c, ret = 1; int c, ret = 1;
int skip_atr = 0; int skip_atr = 0;
int keep_running = 0; int keep_running = 0;
int remote_udp_port = 52342; int remote_udp_port = 52342;
int if_num = 0; int if_num = 0, vendor_id = -1, product_id = -1;
int config_id = -1, altsetting = 0, addr = -1;
char *remote_udp_host = NULL; char *remote_udp_host = NULL;
char *path = NULL;
struct osim_reader_hdl *reader; struct osim_reader_hdl *reader;
struct osim_card_hdl *card; struct osim_card_hdl *card;
@@ -415,7 +611,7 @@ int main(int argc, char **argv)
while (1) { while (1) {
int option_index = 0; int option_index = 0;
c = getopt_long(argc, argv, "r:p:hi:I:ak", opts, &option_index); c = getopt_long(argc, argv, "r:p:hi:V:P:C:I:S:A:H:ak", opts, &option_index);
if (c == -1) if (c == -1)
break; break;
switch (c) { switch (c) {
@@ -432,19 +628,44 @@ int main(int argc, char **argv)
case 'i': case 'i':
gsmtap_host = optarg; gsmtap_host = optarg;
break; break;
case 'I':
if_num = atoi(optarg);
break;
case 'a': case 'a':
skip_atr = 1; skip_atr = 1;
break; break;
case 'k': case 'k':
keep_running = 1; keep_running = 1;
break; break;
case 'V':
vendor_id = strtol(optarg, NULL, 16);
break;
case 'P':
product_id = strtol(optarg, NULL, 16);
break;
case 'C':
config_id = atoi(optarg);
break;
case 'I':
if_num = atoi(optarg);
break;
case 'S':
altsetting = atoi(optarg);
break;
case 'A':
addr = atoi(optarg);
break;
case 'H':
path = optarg;
break;
} }
} }
g_prof = &osim_uicc_sim_cic_profile; if (!remote_udp_host && (vendor_id < 0 || product_id < 0)) {
fprintf(stderr, "You have to specify the vendor and product ID\n");
goto do_exit;
}
transp->udp_fd = -1;
ci->card_prof = &osim_uicc_sim_cic_profile;
if (!remote_udp_host) { if (!remote_udp_host) {
rc = libusb_init(NULL); rc = libusb_init(NULL);
@@ -453,9 +674,10 @@ int main(int argc, char **argv)
goto do_exit; goto do_exit;
} }
} else { } else {
g_udp_fd = osmo_sock_init(AF_INET, SOCK_DGRAM, IPPROTO_UDP, remote_udp_host, transp->udp_fd = osmo_sock_init(AF_INET, SOCK_DGRAM, IPPROTO_UDP,
remote_udp_port+if_num, OSMO_SOCK_F_CONNECT); remote_udp_host, remote_udp_port+if_num,
if (g_udp_fd < 0) { OSMO_SOCK_F_CONNECT);
if (transp->udp_fd < 0) {
fprintf(stderr, "error binding UDP port\n"); fprintf(stderr, "error binding UDP port\n");
goto do_exit; goto do_exit;
} }
@@ -480,8 +702,8 @@ int main(int argc, char **argv)
goto close_exit; goto close_exit;
} }
g_chan = llist_entry(card->channels.next, struct osim_chan_hdl, list); ci->chan = llist_entry(card->channels.next, struct osim_chan_hdl, list);
if (!g_chan) { if (!ci->chan) {
perror("SIM card has no channel?!?"); perror("SIM card has no channel?!?");
goto close_exit; goto close_exit;
} }
@@ -489,47 +711,65 @@ int main(int argc, char **argv)
signal(SIGINT, &signal_handler); signal(SIGINT, &signal_handler);
do { do {
if (g_udp_fd < 0) { if (transp->udp_fd < 0) {
g_devh = libusb_open_device_with_vid_pid(NULL, SIMTRACE_USB_VENDOR, SIMTRACE_USB_PRODUCT); struct usb_interface_match _ifm, *ifm = &_ifm;
if (!g_devh) { ifm->vendor = vendor_id;
ifm->product = product_id;
ifm->configuration = config_id;
ifm->interface = if_num;
ifm->altsetting = altsetting;
ifm->addr = addr;
if (path)
osmo_strlcpy(ifm->path, path, sizeof(ifm->path));
transp->usb_devh = usb_open_claim_interface(NULL, ifm);
if (!transp->usb_devh) {
fprintf(stderr, "can't open USB device\n"); fprintf(stderr, "can't open USB device\n");
goto close_exit; goto close_exit;
} }
rc = libusb_claim_interface(g_devh, if_num); rc = libusb_claim_interface(transp->usb_devh, if_num);
if (rc < 0) { if (rc < 0) {
fprintf(stderr, "can't claim interface %d; rc=%d\n", if_num, rc); fprintf(stderr, "can't claim interface %d; rc=%d\n", if_num, rc);
goto close_exit; goto close_exit;
} }
rc = get_usb_ep_addrs(g_devh, if_num, &g_out_ep, &g_in_ep, NULL); rc = get_usb_ep_addrs(transp->usb_devh, if_num, &transp->usb_ep.out,
&transp->usb_ep.in, &transp->usb_ep.irq_in);
if (rc < 0) { if (rc < 0) {
fprintf(stderr, "can't obtain EP addrs; rc=%d\n", rc); fprintf(stderr, "can't obtain EP addrs; rc=%d\n", rc);
goto close_exit; goto close_exit;
} }
} }
request_card_insert(true); /* simulate card-insert to modem (owhw, not qmod) */
cardem_request_card_insert(ci, true);
/* select remote (forwarded) SIM */
st_modem_sim_select_remote(ci->slot);
/* set the ATR */
uint8_t real_atr[] = { 0x3B, 0x9F, 0x96, 0x80, 0x1F, 0xC7, 0x80, 0x31, uint8_t real_atr[] = { 0x3B, 0x9F, 0x96, 0x80, 0x1F, 0xC7, 0x80, 0x31,
0xA0, 0x73, 0xBE, 0x21, 0x13, 0x67, 0x43, 0x20, 0xA0, 0x73, 0xBE, 0x21, 0x13, 0x67, 0x43, 0x20,
0x07, 0x18, 0x00, 0x00, 0x01, 0xA5 }; 0x07, 0x18, 0x00, 0x00, 0x01, 0xA5 };
atr_update_csum(real_atr, sizeof(real_atr)); atr_update_csum(real_atr, sizeof(real_atr));
request_set_atr(real_atr, sizeof(real_atr)); cardem_request_set_atr(ci, real_atr, sizeof(real_atr));
run_mainloop(); /* select remote (forwarded) SIM */
st_modem_reset_pulse(ci->slot, 300);
run_mainloop(ci);
ret = 0; ret = 0;
if (g_udp_fd < 0) if (transp->udp_fd < 0)
libusb_release_interface(g_devh, 0); libusb_release_interface(transp->usb_devh, 0);
close_exit: close_exit:
if (g_devh) if (transp->usb_devh)
libusb_close(g_devh); libusb_close(transp->usb_devh);
if (keep_running) if (keep_running)
sleep(1); sleep(1);
} while (keep_running); } while (keep_running);
release_exit: if (transp->udp_fd < 0)
if (g_udp_fd < 0)
libusb_exit(NULL); libusb_exit(NULL);
do_exit: do_exit:
return ret; return ret;

78
host/simtrace2_usb.c Normal file
View File

@@ -0,0 +1,78 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include "libusb_util.h"
#define USB_VENDOR_OPENMOKO 0x1d50
#define USB_PRODUCT_OWHW_SAM3_DFU 0x4000
#define USB_PRODUCT_OWHW_SAM3 0x4001
#define USB_PRODUCT_QMOD_HUB 0x4002
#define USB_PRODUCT_QMOD_SAM3_DFU 0x4003
#define USB_PRODUCT_QMOD_SAM3 0x4004
#define USB_PRODUCT_SIMTRACE2_DFU 0x60e2
#define USB_PRODUCT_SIMTRACE2 0x60e3
static const struct dev_id compatible_dev_ids[] = {
{ USB_VENDOR_OPENMOKO, USB_PRODUCT_OWHW_SAM3 },
{ USB_VENDOR_OPENMOKO, USB_PRODUCT_QMOD_SAM3 },
{ USB_VENDOR_OPENMOKO, USB_PRODUCT_SIMTRACE2 },
{ 0, 0 }
};
//libusb_get_string_descriptor_ascii(hdl, idx, *data, len)
static int find_devices(void)
{
struct usb_interface_match ifm[16];
int rc, i, num_interfaces;
rc = usb_match_interfaces(NULL, compatible_dev_ids,
255, 2, -1, ifm, ARRAY_SIZE(ifm));
if (rc < 0)
return rc;
num_interfaces = rc;
for (i = 0; i < num_interfaces; i++) {
struct usb_interface_match *m = &ifm[i];
libusb_device_handle *dev_handle;
char strbuf[256];
printf("\t%04x:%04x Addr=%u, Path=%s, Cfg=%u, Intf=%u, Alt=%u: %d/%d/%d ",
m->vendor, m->product, m->addr, m->path,
m->configuration, m->interface, m->altsetting,
m->class, m->sub_class, m->protocol);
rc = libusb_open(m->usb_dev, &dev_handle);
if (rc < 0) {
printf("\n");
perror("Cannot open device");
continue;
}
rc = libusb_get_string_descriptor_ascii(dev_handle, m->string_idx,
(unsigned char *)strbuf, sizeof(strbuf));
libusb_close(dev_handle);
if (rc < 0) {
printf("\n");
perror("Cannot read string");
continue;
}
printf("(%s)\n", strbuf);
#if 0
dev_handle = usb_open_claim_interface(NULL, m);
printf("dev_handle=%p\n", dev_handle);
libusb_close(dev_handle);
#endif
}
return num_interfaces;
}
int main(int argc, char **argv)
{
libusb_init(NULL);
find_devices();
}

1
host/simtrace_prot.h Symbolic link
View File

@@ -0,0 +1 @@
../firmware/libcommon/include/simtrace_prot.h

View File

@@ -36,7 +36,7 @@
#include <libusb.h> #include <libusb.h>
#include "simtrace.h" #include "simtrace.h"
#include "cardemu_prot.h" #include "simtrace_prot.h"
#include "apdu_dispatch.h" #include "apdu_dispatch.h"
#include "simtrace2-discovery.h" #include "simtrace2-discovery.h"
@@ -142,10 +142,10 @@ static void libusb_fd_added_cb(int fd, short events, void *user_data)
/* call-back when libusb removes a FD */ /* call-back when libusb removes a FD */
static void libusb_fd_removed_cb(int fd, void *user_data) static void libusb_fd_removed_cb(int fd, void *user_data)
{ {
struct osmo_fd *ofd;
printf("%s(%u)\n", __func__, fd); printf("%s(%u)\n", __func__, fd);
#if 0 #if 0
struct osmo_fd *ofd;
/* FIXME: This needs new export in libosmocore! */ /* FIXME: This needs new export in libosmocore! */
ofd = osmo_fd_get_by_fd(fd); ofd = osmo_fd_get_by_fd(fd);
@@ -160,7 +160,7 @@ static void libusb_fd_removed_cb(int fd, void *user_data)
static int ofd_udp_cb(struct osmo_fd *ofd, unsigned int what) static int ofd_udp_cb(struct osmo_fd *ofd, unsigned int what)
{ {
int rc; int rc;
int addrlen = sizeof(g_sa_remote); socklen_t addrlen = sizeof(g_sa_remote);
rc = recvfrom(ofd->fd, g_buf_out.buf, sizeof(g_buf_out.buf), 0, rc = recvfrom(ofd->fd, g_buf_out.buf, sizeof(g_buf_out.buf), 0,
(struct sockaddr *)&g_sa_remote, &addrlen); (struct sockaddr *)&g_sa_remote, &addrlen);
@@ -204,7 +204,6 @@ int main(int argc, char **argv)
{ {
int rc; int rc;
int c, ret = 1; int c, ret = 1;
char *remote_host = NULL;
int local_udp_port = 52342; int local_udp_port = 52342;
unsigned int if_num = 0; unsigned int if_num = 0;
@@ -282,7 +281,6 @@ close_exit:
if (g_devh) if (g_devh)
libusb_close(g_devh); libusb_close(g_devh);
release_exit:
libusb_exit(NULL); libusb_exit(NULL);
return ret; return ret;
} }

16
notes
View File

@@ -1,16 +0,0 @@
* MCK auf irgendwas <= 64MHz --> fehler v. baudrate minimal(+- 3%) (script) und baudrate sinnvoll
* flash: wait states?
* Pll_frequ*MUL <= 130, sonst schlecht
* ordner umordnen
_* kleine libc (baselibc) reinmachen,
* printf "einstellen" (uart)
* simkarten-protokoll (iso7816-3) anschauen
(buchempfehlung: rankl-effing: "chipkartenhandbuch")
* git: remove trailing whitespaces configurieren
Ganz später:
* am besten oben in jede datei schreiben: based on atmel example ...
* am besten in jede datei
*

View File

@@ -1,150 +0,0 @@
#!/usr/bin/env python
# Code ported from simtrace host program apdu_split.c
#
# (C) 2010 by Harald Welte <hwelte@hmw-consulting.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2
# as published by the Free Software Foundation
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
from enum import Enum
from util import HEX
from array import array
class apdu_states(Enum):
APDU_S_CLA = 1
APDU_S_INS = 2
APDU_S_P1 = 3
APDU_S_P2 = 4
APDU_S_P3 = 5
APDU_S_SEND_DATA = 6
APDU_S_DATA = 7
APDU_S_DATA_SINGLE = 8
APDU_S_SW1 = 9
APDU_S_SW2 = 10
APDU_S_FIN = 11
PTS = 12
class Apdu_splitter:
def __init__(self):
self.state = apdu_states.APDU_S_CLA
self.buf = array('B', [])
self.pts_buf = array('B', [])
self.data = array('B', [])
self.ins = array('B', [])
self.data_remainig = 0
def func_APDU_S_INS(self, c):
self.ins = c
self.buf.append(c)
self.state = apdu_states(self.state.value + 1)
def func_PTS(self, c):
self.pts_buf.append(c)
print("PTS: ", self.pts_buf)
if self.pts_buf == [0xff, 0x00, 0xff]:
self.state = apdu_states.APDU_S_FIN
def func_APDU_S_CLA_P1_P2(self, c):
if self.state == apdu_states.APDU_S_CLA and c == 0xff:
self.state = apdu_states.PTS
self.pts_buf = [c]
else:
self.buf.append(c)
self.state = apdu_states(self.state.value + 1)
def func_APDU_S_P3(self, c):
self.buf.append(c)
self.data_remaining = 256 if c == 0 else c
self.state = apdu_states.APDU_S_SW1
def func_APDU_S_DATA(self, c):
self.buf.append(c)
self.data.append(c)
self.data_remaining -= 1
if self.data_remaining == 0:
self.state = apdu_states.APDU_S_SW1
def func_APDU_S_DATA_SINGLE(self, c):
self.buf.append(c)
self.data_remaining -= 1
self.state = apdu_states.APDU_S_SW1
def func_APDU_S_SW1(self, c):
if (c == 0x60):
print("APDU_S_SW1: NULL")
else:
# check for 'all remaining' type ACK
if c == self.ins or c == self.ins + 1 or c == ~(self.ins+1):
print("ACK")
self.data = []
if self.ins in self.INS_data_expected:
self.state = apdu_states.APDU_S_SEND_DATA
else:
self.state = apdu_states.APDU_S_DATA
else:
# check for 'only next byte' type ACK */
if c == ~(self.ins):
self.state = apdu_states.APDU_S_DATA_SINGLE
else:
# must be SW1
self.sw1 = c
self.buf.append(c)
self.state = apdu_states.APDU_S_SW2
def func_APDU_S_SW2(self, c):
self.buf.append(c)
self.sw2 = c
print("APDU:", HEX(self.ins), HEX(self.buf))
self.state = apdu_states.APDU_S_FIN
Apdu_S = {
apdu_states.APDU_S_CLA : func_APDU_S_CLA_P1_P2,
apdu_states.APDU_S_INS : func_APDU_S_INS,
apdu_states.APDU_S_P1 : func_APDU_S_CLA_P1_P2,
apdu_states.APDU_S_P2 : func_APDU_S_CLA_P1_P2,
apdu_states.APDU_S_P3 : func_APDU_S_P3,
apdu_states.APDU_S_SEND_DATA : func_APDU_S_DATA,
apdu_states.APDU_S_DATA : func_APDU_S_DATA,
apdu_states.APDU_S_DATA_SINGLE : func_APDU_S_DATA_SINGLE,
apdu_states.APDU_S_SW1 : func_APDU_S_SW1,
apdu_states.APDU_S_SW2 : func_APDU_S_SW2,
apdu_states.PTS : func_PTS }
INS_data_expected = [0xC0, 0xB0, 0xB2, 0x12, 0xF2]
def split(self, c):
# if c == 0xA0:
# self.state = apdu_states.APDU_S_CLA
print("state: ", self.state, hex(c))
self.Apdu_S[self.state](self, c)
if __name__ == '__main__':
msg1 = [0xA0, 0xA4, 0x00, 0x00, 0x02, 0xA4, 0x7F, 0x20, 0x9F, 0x16]
msg2 = [0xA0, 0xC0, 0x00, 0x00, 0x16, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x7F, 0x20,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x09, 0x91, 0x00, 0x17, 0x04, 0x00, 0x00, 0x00,
0x83, 0x8A, 0x90, 0x00]
msg3 = [0xa0, 0xc0, 0x00, 0x00, 0x16, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x7f,
0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x91, 0x00, 0x17,
0x04, 0x00, 0x83, 0x8a, 0x83, 0x8a, 0x90]
pts = [0xff, 0x00, 0xff]
apdus = []
apdu = Apdu_splitter()
for c in pts + msg2 + msg1 + msg3:
apdu.split(c)
if apdu.state == apdu_states.APDU_S_FIN:
apdus.append(apdu)
apdu = Apdu_splitter()
for a in apdus:
print(HEX(a.buf))

View File

@@ -1,102 +0,0 @@
#!/usr/bin/env python
from smartcard.scard import *
import smartcard.util
import array
from util import HEX
class SmartcardException(Exception):
pass
class SmartcardConnection:
# hcard, dwActiveProtocol, hcontext, reader
def __init__(self):
self.establish_context()
self.connect_card()
def getATR(self):
hresult, reader, state, protocol, atr = SCardStatus(self.hcard)
if hresult != SCARD_S_SUCCESS:
print 'failed to get status: ' + SCardGetErrorMessage(hresult)
print 'Reader:', reader
print 'State:', state
print 'Protocol:', protocol
print 'ATR:', HEX(atr)
return array.array('B', atr)
def reset_card(self):
hresult, self.dwActiveProtocol = SCardReconnect(self.hcard, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD)
if hresult != SCARD_S_SUCCESS:
raise SmartcardException('Unable to retrieve Atr: ' +
SCardGetErrorMessage(hresult))
def connect_card(self):
hresult, self.hcard, self.dwActiveProtocol = SCardConnect(self.hcontext, self.reader,
SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)
if hresult != SCARD_S_SUCCESS:
raise SmartcardException('Unable to connect: ' +
SCardGetErrorMessage(hresult))
print 'Connected with active protocol', self.dwActiveProtocol
def establish_context(self):
hresult, self.hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
raise SmartcardException('Failed to establish context : ' +
SCardGetErrorMessage(hresult))
print 'Context established!'
hresult, readers = SCardListReaders(self.hcontext, [])
if hresult != SCARD_S_SUCCESS:
raise SmartcardException('Failed to list readers: ' +
SCardGetErrorMessage(hresult))
print 'PCSC Readers:', readers
if len(readers) < 1:
raise SmartcardException('No smart card readers')
self.reader = readers[0]
print "Using reader:", self.reader
def release_context(self):
hresult = SCardReleaseContext(self.hcontext)
if hresult != SCARD_S_SUCCESS:
raise SmartcardException('Failed to release context: ' +
SCardGetErrorMessage(hresult))
print 'Released context.'
def send_receive_cmd(self, cmd):
print("Cmd to SIM: " + HEX(cmd))
hresult, resp = SCardTransmit(self.hcard, self.dwActiveProtocol,
cmd.tolist())
if hresult != SCARD_S_SUCCESS:
raise SmartcardException('Failed to transmit: ' +
SCardGetErrorMessage(hresult))
print 'SIM Ans: ' + HEX(resp)
return array.array('B', resp)
def disconnect_card(self):
hresult = SCardDisconnect(self.hcard, SCARD_UNPOWER_CARD)
if hresult != SCARD_S_SUCCESS:
raise SmartcardException('Failed to disconnect: ' +
SCardGetErrorMessage(hresult))
print 'Disconnected'
def close(self):
self.disconnect_card()
self.release_context()
if __name__ == '__main__':
import constants
sm_con = SmartcardConnection()
sm_con.getATR()
print(sm_con.send_receive_cmd(constants.CMD_SEL_ROOT))
print(sm_con.send_receive_cmd(constants.CMD_SEL_FILE))
print(sm_con.send_receive_cmd(constants.CMD_GET_DATA))
sm_con.close()

View File

@@ -1,74 +0,0 @@
#!/usr/bin/env python
# This is only test code for testing the smartcard communication
# by sending a test command and printing the answer of the smartcard.
from smartcard.scard import *
import smartcard.util
SELECT = [0xA0, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00]
COMMAND = [0x00, 0x00, 0x00, 0x00]
def select():
try:
hresult, hcontext = SCardEstablishContext(SCARD_SCOPE_USER)
if hresult != SCARD_S_SUCCESS:
raise Exception('Failed to establish context : ' +
SCardGetErrorMessage(hresult))
print 'Context established!'
try:
hresult, readers = SCardListReaders(hcontext, [])
if hresult != SCARD_S_SUCCESS:
raise Exception('Failed to list readers: ' +
SCardGetErrorMessage(hresult))
print 'PCSC Readers:', readers
if len(readers) < 1:
raise Exception('No smart card readers')
reader = readers[0]
print "Using reader:", reader
try:
hresult, hcard, dwActiveProtocol = SCardConnect(hcontext, reader,
SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)
if hresult != SCARD_S_SUCCESS:
raise Exception('Unable to connect: ' +
SCardGetErrorMessage(hresult))
print 'Connected with active protocol', dwActiveProtocol
try:
hresult, response = SCardTransmit(hcard, dwActiveProtocol,
SELECT)
if hresult != SCARD_S_SUCCESS:
raise Exception('Failed to transmit: ' +
SCardGetErrorMessage(hresult))
print 'Select: ' + smartcard.util.toHexString(response,
smartcard.util.HEX)
hresult, response = SCardTransmit(hcard, dwActiveProtocol,
COMMAND)
if hresult != SCARD_S_SUCCESS:
raise Exception('Failed to transmit: ' +
SCardGetErrorMessage(hresult))
print 'Command: ' + smartcard.util.toHexString(response,
smartcard.util.HEX)
finally:
hresult = SCardDisconnect(hcard, SCARD_UNPOWER_CARD)
if hresult != SCARD_S_SUCCESS:
raise Exception('Failed to disconnect: ' +
SCardGetErrorMessage(hresult))
print 'Disconnected'
except Exception, message:
print "Exception:", message
finally:
hresult = SCardReleaseContext(hcontext)
if hresult != SCARD_S_SUCCESS:
raise Exception('Failed to release context: ' +
SCardGetErrorMessage(hresult))
print 'Released context.'
except Exception, message:
print "Exception:", message

View File

@@ -1,42 +0,0 @@
from array import array
SIM_WR = 0x1
SIM_RD = 0x82
SIM_INT = 0x83
PHONE_WR = 0x4
PHONE_RD = 0x85
PHONE_INT = 0x86
# Change FIDI
CMD_CHANGE_FIDI = array('B', [0xff, 0x00, 0xff])
CHANGE_FIDI = array('B', [0xff, 0xff, 0xff, 0x00, 0xff])
# Phone book
PHONE_BOOK_REQ = array('B', [0xa0, 0xb2, 0x01, 0x04, 0x1a])
PHONE_BOOK_RESP = array('B', [0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0xff, 0xff, 0xff, 0xff, 0x09, 0x81, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xff, 0xff, 0xff, 0xff, 0x90, 0x00])
PHONE_BOOK_RESP_MITM = array('B', [0x6d, 0x69, 0x74, 0x6d, 0x20, 0x21, 0x21, 0x21, 0xff, 0xff, 0xff, 0xff, 0x09, 0x81, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xff, 0xff, 0xff, 0xff, 0x90, 0x00])
PHONE_BOOK_RESP2 = array('B', [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0x00])
PHONE_BOOK_RESP_MITM2 = array('B', [0x6d, 0x69, 0x74, 0x6d, 0x20, 0x21, 0x21, 0x21, 0xff, 0xff, 0xff, 0xff, 0x09, 0x81, 0x22, 0x22,
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xff, 0xff, 0xff, 0xff, 0x90, 0x00])
CMD_SEL_ROOT = array('B', [0xA0, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00])
CMD_SEL_FILE = array('B', [0xA0, 0xA4, 0x00, 0x00, 0x02, 0x7F, 0x20])
CMD_GET_DATA = array('B', [0xA0, 0xC0, 0x00, 0x00, 0x16])
# SuperSIM ATR
ATR_SUPERSIM= array('B', [0x3B, 0x9A, 0x94, 0x00, 0x92, 0x02, 0x75, 0x93, 0x11, 0x00, 0x01, 0x02, 0x02, 0x19])
# Faster sysmocom SIM
ATR_SYSMOCOM1 = array('B', [0x3B, 0x99, 0x18, 0x00, 0x11, 0x88, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x60])
ATR_SYSMOCOM2 = array('B', [0x3B, 0x99, 0x11, 0x00, 0x11, 0x88, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x60])
ATR_SYSMOCOM3 = array('B', [0x3B, 0x99, 0x44, 0x00, 0x11, 0x88, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x60])
NEW_ATR = ATR_SYSMOCOM1
ATR_STRANGE_SIM = array('B', [0x3B, 0x0B, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x2E, 0x00, 0x20, 0x68])
# USB errors
ERR_TIMEOUT = 110
ERR_NO_SUCH_DEV = 19

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env python
import socket
import array
ip="127.0.0.1"
port=4729
sp=58621
gsmtap_hdr="\x02\x04\x04"+"\x00"*13
# FIXME: Is ATR something special?
def gsmtap_send_apdu(data):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((ip, port))
s.send(gsmtap_hdr+data.tostring())
s.close()
if __name__ == '__main__':
cmds = ("\x3B\x99\x18\x00\x11\x88\x22\x33\x44\x55\x66\x77\x60", # ATR
"\xa0\xa4\x00\x00\x02\x6f\x7e\x9f\x0f", # SELECT FILE
"\xa0\xd6\x00\x00\x0b\xff\xff\xff\xff\x09\xf1\x07\xff\xfe\x00\x03\x90\x00", # UPDATE BINARY
)
for cmd in cmds:
gsmtap_send_apdu(array.array('B', cmd))

View File

@@ -1,98 +0,0 @@
import usb.core
import usb.util
import array
from ccid_raw import SmartcardConnection
from smartcard_emulator import SmartCardEmulator
from gsmtap import gsmtap_send_apdu
from contextlib import closing
from util import HEX
from constants import *
from apdu_split import Apdu_splitter, apdu_states
from replace import replace
def pattern_match(inpt):
print("Matching inpt", inpt)
if (inpt == ATR_SYSMOCOM1) or (inpt == ATR_STRANGE_SIM):
print("ATR: ", inpt)
return NEW_ATR
elif (inpt == CMD_SEL_FILE):
print("CMD_SEL_FILE:", inpt)
return CMD_SEL_ROOT
elif (inpt == CMD_GET_DATA):
print("CMD_DATA:", inpt)
return CMD_SEL_ROOT
else:
return inpt
def poll_ep(dev, ep):
try:
return dev.read(ep, 64, 10)
except usb.core.USBError as e:
if e.errno != ERR_TIMEOUT:
raise
return None
def write_phone(dev, resp):
print("WR: ", HEX(resp))
dev.write(PHONE_WR, resp, 10)
def do_mitm(dev, sim_emul=True):
if sim_emul == True:
my_class = SmartCardEmulator
else:
my_class = SmartcardConnection
with closing(my_class()) as sm_con:
atr = sm_con.getATR()
apdus = []
apdu = Apdu_splitter()
while True:
cmd = poll_ep(dev, PHONE_INT)
if cmd is not None:
print("Int line ", HEX(cmd))
assert cmd[0] == ord('R')
# FIXME: restart card anyways?
# sm_con.reset_card()
print("Write atr: ", HEX(atr))
write_phone(dev, replace(atr))
apdus = []
apdu = Apdu_splitter()
cmd = poll_ep(dev, PHONE_RD)
if cmd is not None:
print("RD: ", HEX(cmd))
for c in cmd:
if apdu.state == apdu_states.APDU_S_FIN:
apdus.append(apdu)
gsmtap_send_apdu(apdu.buf)
apdu = Apdu_splitter()
apdu.split(c)
if apdu.state == apdu_states.APDU_S_FIN and apdu.pts_buf == [0xff, 0x00, 0xff]:
#sim_data = sm_con.send_receive_cmd(apdu.pts_buf)
#write_phone(dev, replace(array('B', sim_data)))
write_phone(dev, replace(array('B', apdu.pts_buf)))
continue;
if apdu.state == apdu_states.APDU_S_SW1:
if apdu.data is not None and len(apdu.data) == 0:
# FIXME: implement other ACK types
write_phone(dev, replace(array('B', [apdu.ins])))
apdu.split(apdu.ins)
else:
sim_data = sm_con.send_receive_cmd(apdu.buf)
write_phone(dev, replace(sim_data))
for c in sim_data:
apdu.split(c)
if apdu.state == apdu_states.APDU_S_SEND_DATA:
sim_data = sm_con.send_receive_cmd(replace(apdu.buf))
#sim_data.insert(0, apdu.ins)
write_phone(dev, replace(sim_data))
#apdu.state = apdu_states.APDU_S_SW1
for c in sim_data:
apdu.split(c)

View File

@@ -1,74 +0,0 @@
#!/usr/bin/env python
from enum import Enum
# FIXME: response!
class pts_states(Enum):
PTSS = 0
PTS0 = 1
PTS1 = 2
PTS2 = 3
PTS3 = 4
PCK = 5
class Pts_splitter:
def __init__(self):
self.state = pts_states.PTSS
self.buf = []
def func_PTS_PTSS(self, ptss):
self.state = pts_states.PTS0
def func_PTS_PTS0(self, pts0):
self.pts0 = pts0
self.state = pts_states.PTS1
def func_PTS_PTS1(self, fidi):
print("pts0: ", self.pts0)
if (self.pts0 & 1<<4) != 0:
self.fidi = fidi
print("FiDi: ", fidi)
self.state = pts_states.PTS2
else:
self.PTS_S[pts_states.PTS2](self, c)
def func_PTS_PTS2(self, c):
if (self.pts0 & 1<<5) != 0:
print("ETU: ", c)
self.state = pts_states.PTS3
else:
self.PTS_S[pts_states.PTS3](self, c)
def func_PTS_PTS3(self, c):
if (self.pts0 & 1<<6) != 0:
print("RFU: ", c)
self.state = pts_states.PCK
else:
self.PTS_S[pts_states.PCK](self, c)
def func_PTS_PCK(self, c):
print("PCK: ", c)
self.state = pts_states.PCK
PTS_S = {
pts_states.PTSS : func_PTS_PTSS,
pts_states.PTS0 : func_PTS_PTS0,
pts_states.PTS1 : func_PTS_PTS1,
pts_states.PTS2 : func_PTS_PTS2,
pts_states.PTS3 : func_PTS_PTS3,
pts_states.PCK : func_PTS_PCK
}
def split(self, c):
self.PTS_S[self.state](self, c)
if __name__ == '__main__':
pts_msg1 = [0xff, 0x00, 0xff]
pts_msg_fidi = [0xff, (1<<4), 0x18, 0xff]
pts_msg_fidi_etu = [0xff, ((1<<4) | (1<<5)), 0x18, 0x01, 0xff]
pts = Pts_splitter()
for c in pts_msg1 + pts_msg_fidi + pts_msg_fidi_etu:
pts.split(c)
if (pts.state == pts_states.PCK):
pts = Pts_splitter()

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env python
import array
from constants import *
# Address book entries
name = 'deine mudda'
phone = '0123456789abcdef'
def replace(data):
print(replace.last_req)
if data is None:
raise MITMReplaceError
else:
try:
if data[0] == 0xA0:
print("INS: ", hex(data[1]))
replace.last_req = data
return data
if data[0] == 0x3B:
return data
#print("*** Replace ATR")
#return array('B', NEW_ATR)
elif data[0] == 0x9F:
return data
# print("*** Replace return val")
# return array('B', [0x60, 0x00])
elif replace.last_req[1:5] == array('B', [0xB2, 0x01, 0x04, 0x1A]): # phone book request
print("*** Replace phone book")
# return array('B', [0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xff, 0xff, 0xff, 0xff, 0x09, 0x81, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff, 0x90, 0x00])
resp = map(ord, name) + ([0xff]*(12-len(name))) + [len(name) + 1] + [0x81]
for x in range(1,len(phone)/2+1):
list.append(resp, int(phone[x*2-2:2*x:], 16))
resp += ([0xff]*(replace.last_req[4]-len(resp))) + [0x90, 0x00]
return array('B', resp)
except ValueError:
print("*** Value error! ")
return data
replace.last_req = array('B')
if __name__ == '__main__':
print("Replacing PHONE_BOOK_REQ", PHONE_BOOK_REQ, "with", replace(PHONE_BOOK_REQ))
print("Replacing PHONE_BOOK_RESP", PHONE_BOOK_RESP, "with", replace(PHONE_BOOK_RESP))

View File

@@ -1,4 +0,0 @@
argparse==1.2.1
pyscard==1.6.12
pyusb==1.0.0b2
wsgiref==0.1.2

View File

@@ -1,327 +0,0 @@
 [?1l>
k./sniffer.py\Found device
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 3b 9a 94 00 92 02 75 93 11 00 01 02 02 19 a0 a4 00 00 02 a4 7f 20 9f 16 a0 c0 00 00 16 c0 00 00 00 00 7f 20 02 00 00 00 00 00 09 91 00 17
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 3b 9a 94 00 92 02 75 93 11 00 01 02 02 19 a0 a4 00 00 02 a4 7f 20 9f 16 a0 c0 00 00 16 c0 00 00 00 00 7f 20 02 00 00 00 00 00 09 91 00 17
00 00 02 a4 3f 00 9f 16 a0 a4 00 00 02 a4 7f 20 9f 16 a0 c0 00 00 16 c0 00 00 00 00 7f 20 02 00 00 00 00 00 09 91 00 17 04 00 83 8a 83 8a 90 00 a0 a4 00 00 02 a4 6f b7 94 04 a0 a4 00 00 02 a4
00 00 02 a4 3f 00 9f 16 a0 a4 00 00 02 a4 7f 20 9f 16 a0 c0 00 00 16 c0 00 00 00 00 7f 20 02 00 00 00 00 00 09 91 00 17 04 00 83 8a 83 8a 90 00 a0 a4 00 00 02 a4 6f b7 94 04 a0 a4 00 00 02 a4
7f ff ff ff 37 00 00 3f 06 91 2c a0 a4 00 00 02 a4 6f 7e 9f 0f a0 c0 00 00 0f c0 00 00 00 0b 6f 7e 04 00 11 ff 1f 01 02 00 00 91 2c a0 a4 00 00 02 a4 6f 07 9f 0f a0 c0 00 00 0f c0 00 00 00 09
7f ff ff ff 37 00 00 3f 06 91 2c a0 a4 00 00 02 a4 6f 7e 9f 0f a0 c0 00 00 0f c0 00 00 00 0b 6f 7e 04 00 11 ff 1f 01 02 00 00 91 2c a0 a4 00 00 02 a4 6f 07 9f 0f a0 c0 00 00 0f c0 00 00 00 09
a0 a4 00 00 02 a4 6f ad 9f 0f a0 b0 00 00 01 b0 00 91 2c a0 a4 00 00 02 a4 6f 07 9f 0f a0 b0 00 00 09 b0 08 99 10 07 32 19 42 88 91 91 2c a0 a4 00 00 02 a4 6f 7e 9f 0f a0 b0 00 00 0b b0 ff ff
a0 a4 00 00 02 a4 6f ad 9f 0f a0 b0 00 00 01 b0 00 91 2c a0 a4 00 00 02 a4 6f 07 9f 0f a0 b0 00 00 09 b0 08 99 10 07 32 19 42 88 91 91 2c a0 a4 00 00 02 a4 6f 7e 9f 0f a0 b0 00 00 0b b0 ff ff
2c a0 a4 00 00 02 a4 6f 20 9f 0f a0 b0 00 00 09 b0 40 75 d4 84 36 01 b4 00 01 91 2c a0 a4 00 00 02 a4 6f 30 9f 0f a0 c0 00 00 0f c0 00 00 00 2d 6f 30 04 00 11 ff ff 01 02 00 00 91 2c a0 b0 00
2c a0 a4 00 00 02 a4 6f 20 9f 0f a0 b0 00 00 09 b0 40 75 d4 84 36 01 b4 00 01 91 2c a0 a4 00 00 02 a4 6f 30 9f 0f a0 c0 00 00 0f c0 00 00 00 2d 6f 30 04 00 11 ff ff 01 02 00 00 91 2c a0 b0 00
0c b0 32 f4 03 32 f4 33 32 f4 01 ff ff ff 91 2c a0 a4 00 00 02 a4 6f 31 9f 0f a0 b0 00 00 01 b0 05 91 2c a0 a4 00 00 02 a4 6f 39 9f 0f a0 c0 00 00 0f c0 00 00 00 1e 6f 39 04 40 12 1f ff 01 02
0c b0 32 f4 03 32 f4 33 32 f4 01 ff ff ff 91 2c a0 a4 00 00 02 a4 6f 31 9f 0f a0 b0 00 00 01 b0 05 91 2c a0 a4 00 00 02 a4 6f 39 9f 0f a0 c0 00 00 0f c0 00 00 00 1e 6f 39 04 40 12 1f ff 01 02
01 02 00 00 91 2c a0 a4 00 00 02 a4 6f 48 94 04 a0 a4 00 00 02 a4 6f 10 94 04 a0 a4 00 00 02 a4 7f 10 9f 16 a0 a4 00 00 02 a4 6f 43 9f 0f a0 b0 00 00 02 b0 09 ff 91 2c a0 12 00 00 2c 12 d0 2a
01 02 00 00 91 2c a0 a4 00 00 02 a4 6f 48 94 04 a0 a4 00 00 02 a4 6f 10 94 04 a0 a4 00 00 02 a4 7f 10 9f 16 a0 a4 00 00 02 a4 6f 43 9f 0f a0 b0 00 00 02 b0 09 ff 91 2c a0 12 00 00 2c 12 d0 2a
a0 a4 00 00 02 a4 6f 11 94 04 a0 a4 00 00 02 a4 6f 39 9f 0f a0 c0 00 00 0f c0 00 00 00 1e 6f 39 04 40 12 1f ff 01 02 03 03 90 00 a0 b2 01 04 03 b2 00 00 00 90 00 a0 a4 00 00 02 a4 6f b7 94 04
a0 a4 00 00 02 a4 6f 11 94 04 a0 a4 00 00 02 a4 6f 39 9f 0f a0 c0 00 00 0f c0 00 00 00 1e 6f 39 04 40 12 1f ff 01 02 03 03 90 00 a0 b2 01 04 03 b2 00 00 00 90 00 a0 a4 00 00 02 a4 6f b7 94 04
a0 c0 00 00 0f c0 00 00 00 11 6f 46 04 00 0b ff ff 01 02 00 00 90 00 a0 b0 00 00 11 b0 01 4f 70 65 6e 42 53 43 ff ff ff ff ff ff ff ff ff 90 00 a0 a4 00 00 02 a4 6f 07 9f 0f a0 c0 00 00 0f c0
a0 c0 00 00 0f c0 00 00 00 11 6f 46 04 00 0b ff ff 01 02 00 00 90 00 a0 b0 00 00 11 b0 01 4f 70 65 6e 42 53 43 ff ff ff ff ff ff ff ff ff 90 00 a0 a4 00 00 02 a4 6f 07 9f 0f a0 c0 00 00 0f c0
a0 c0 00 00 0f c0 00 00 00 03 6f 37 04 00 12 ff ff 01 02 00 00 90 00 a0 b0 00 00 03 b0 00 00 00 90 00 a0 a4 00 00 02 a4 7f 10 9f 16 a0 a4 00 00 02 a4 6f 3c 9f 0f a0 c0 00 00 0f c0 00 00 1b 80
a0 c0 00 00 0f c0 00 00 00 03 6f 37 04 00 12 ff ff 01 02 00 00 90 00 a0 b0 00 00 03 b0 00 00 00 90 00 a0 a4 00 00 02 a4 7f 10 9f 16 a0 a4 00 00 02 a4 6f 3c 9f 0f a0 c0 00 00 0f c0 00 00 1b 80
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff 90 00 a0 a4 00 00 02 a4 7f 20 9f 16 a0 a4 00 00 02 a4 6f 41 9f 0f a0 c0 00 00 0f c0 00 00 00 05 6f 41 04 00 12 ff ff 01 02 00 00 90 00 a0 b0 00 00 05 b0 ff ff ff 00 00 90 00 a0 a4 00
ff ff ff 90 00 a0 a4 00 00 02 a4 7f 20 9f 16 a0 a4 00 00 02 a4 6f 41 9f 0f a0 c0 00 00 0f c0 00 00 00 05 6f 41 04 00 12 ff ff 01 02 00 00 90 00 a0 b0 00 00 05 b0 ff ff ff 00 00 90 00 a0 a4 00
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 05 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 05 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
a0 b2 07 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
a0 b2 07 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 08 04 b0 b2 00 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 08 04 b0 b2 00 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0a 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0a 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0c 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0c 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff 90 00 a0 b2 0e 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff 90 00 a0 b2 0e 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 11 04 b0 b2 00 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 11 04 b0 b2 00 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 13 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 13 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 15 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 15 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
a0 b2 17 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
a0 b2 17 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 18 04 b0 b2 00 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 18 04 b0 b2 00 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1a 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1a 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1c 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1c 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff 90 00 a0 b2 1e 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff 90 00 a0 b2 1e 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 21 04 b0 b2 00 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 21 04 b0 b2 00 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 23 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 23 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 25 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 25 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
a0 b2 27 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
a0 b2 27 04 b0 b2 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 28 04 b0 b2 00 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 28 04 b0 b2 00 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 00 00 50 6f 42 04 00 11 ff ff 01 02 01 28 90 00 a0 b2 01 04 28 b2 ff ff ff ff ff ff ff ff ff ff ff ff e1 ff ff ff ff ff ff ff ff ff ff ff ff 05 81 00 94 55 55 ff ff ff ff ff ff 00 00 00 90
00 00 00 50 6f 42 04 00 11 ff ff 01 02 01 28 90 00 a0 b2 01 04 28 b2 ff ff ff ff ff ff ff ff ff ff ff ff e1 ff ff ff ff ff ff ff ff ff ff ff ff 05 81 00 94 55 55 ff ff ff ff ff ff 00 00 00 90
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 a4 00 00 02 a4 7f 10 9f 16 a0 a4 00 00 02 a4 6f 3a 9f 0f a0 c0 00 00 0f c0 00 00 19 64 6f 3a 04 00 11 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 a4 00 00 02 a4 7f 10 9f 16 a0 a4 00 00 02 a4 6f 3a 9f 0f a0 c0 00 00 0f c0 00 00 19 64 6f 3a 04 00 11 ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 04 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 04 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 07 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 08 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 07 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 08 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0b 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0c
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0b 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0c
00 a0 b2 0e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 a0 b2 0e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 0f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 12 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 13 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 12 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 13 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 16 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 17 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 16 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 17 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1a 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1b 04 1a
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1a 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1b 04 1a
b2 1d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
b2 1d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 1e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 21 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 22 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 21 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 22 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 25 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 26 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 25 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 26 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 29 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 2a 04 1a b2 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 29 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 2a 04 1a b2 ff
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 2d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 2d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
ff ff ff 90 00 a0 b2 30 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 31 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff 90 00 a0 b2 30 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 31 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 34 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 35 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 34 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 35 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 38 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 39 04 1a b2 ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 38 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 39 04 1a b2 ff ff ff
b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 3c 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 3c 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
ff 90 00 a0 b2 3f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 40 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff 90 00 a0 b2 3f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 40 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 43 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 44 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 43 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 44 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 47 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 48 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 47 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 48 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 4b 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 4c
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 4b 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 4c
00 a0 b2 4e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 4f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 a0 b2 4e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 4f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 52 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 53 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 52 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 53 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 56 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 57 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 56 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 57 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 5a 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 5b 04 1a
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 5a 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 5b 04 1a
b2 5d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 5e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
b2 5d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 5e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 61 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 62 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 61 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 62 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 65 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 66 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 65 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 66 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 69 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 6a 04 1a b2 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 69 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 6a 04 1a b2 ff
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 6d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 6d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
ff ff ff 90 00 a0 b2 70 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 71 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff 90 00 a0 b2 70 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 71 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 74 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 75 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 74 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 75 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 78 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 79 04 1a b2 ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 78 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 79 04 1a b2 ff ff ff
b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 7c 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 7c 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
ff 90 00 a0 b2 7f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 80 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff 90 00 a0 b2 7f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 80 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 83 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 84 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 83 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 84 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 87 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 88 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 87 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 88 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 8b 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 8c
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 8b 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 8c
00 a0 b2 8e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 8f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 a0 b2 8e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 8f 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 92 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 93 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 92 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 93 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 96 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 97 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 96 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 97 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 9a 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 9b 04 1a
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 9a 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 9b 04 1a
b2 9d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 9e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
b2 9d 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 9e 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 a1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a2 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 a1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a2 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a6 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a6 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a9 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 aa 04 1a b2 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 a9 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 aa 04 1a b2 ff
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 ad 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 ad 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
ff ff ff 90 00 a0 b2 b0 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff 90 00 a0 b2 b0 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b4 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b4 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b8 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b9 04 1a b2 ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b8 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 b9 04 1a b2 ff ff ff
b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 bc 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 bc 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
ff 90 00 a0 b2 bf 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c0 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff 90 00 a0 b2 bf 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c0 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c3 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c4 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c3 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c4 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c7 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c8 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c7 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 c8 04 1a b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 cb 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 cc
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 cb 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 cc
00 a0 b2 ce 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 cf 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 a0 b2 ce 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 cf 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 d2 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 d3 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff 90 00 a0 b2 d2 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 d3 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 d6 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 d7 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 d6 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 d7 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 da 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 db 04 1a
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 da 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 db 04 1a
b2 dd 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 de 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
b2 dd 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 de 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 e1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e2 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff 90 00 a0 b2 e1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e2 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e6 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e6 04 1a b2 ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e9 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 ea 04 1a b2 ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 e9 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 ea 04 1a b2 ff
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 ed 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 ed 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90
ff ff ff 90 00 a0 b2 f0 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff 90 00 a0 b2 f0 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f1 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f4 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f4 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f5 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f8 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f9 04 1a b2 ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f8 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 f9 04 1a b2 ff ff ff
a4 6f 3b 9f 0f a0 c0 00 00 0f c0 00 00 01 04 6f 3b 04 00 12 ff ff 01 02 01 1a 90 00 a0 b2 01 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2
a4 6f 3b 9f 0f a0 c0 00 00 0f c0 00 00 01 04 6f 3b 04 00 12 ff ff 01 02 01 1a 90 00 a0 b2 01 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2
90 00 a0 b2 04 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 05 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
90 00 a0 b2 04 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 05 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 90 00 a0 b2 08 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 09 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff 90 00 a0 b2 08 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 09 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 00 00 34 6f 40 04 00 11 ff ff 01 02 01 1a 90 00 a0 b2 01 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 02 04 1a b2 ff ff ff ff ff ff ff
00 00 00 34 6f 40 04 00 11 ff ff 01 02 01 1a 90 00 a0 b2 01 04 1a b2 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 02 04 1a b2 ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 02 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 04 04 0d b2 ff ff ff ff ff
ff ff ff ff ff ff ff ff ff 90 00 a0 b2 02 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 04 04 0d b2 ff ff ff ff ff
a0 b2 01 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 02 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
a0 b2 01 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 02 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0 b2 03 04 0d b2 ff ff ff ff ff ff ff ff ff ff ff ff ff 90 00 a0
00 11 ff ff 01 02 01 28 90 00 a0 dc 01 04 28 dc ff ff ff ff ff ff ff ff ff ff ff ff e1 ff ff ff ff ff ff ff ff ff ff ff ff 05 81 00 94 55 55 ff ff ff ff ff ff 00 00 00 90 00 a0 a4 00 00 02 a4
00 11 ff ff 01 02 01 28 90 00 a0 dc 01 04 28 dc ff ff ff ff ff ff ff ff ff ff ff ff e1 ff ff ff ff ff ff ff ff ff ff ff ff 05 81 00 94 55 55 ff ff ff ff ff ff 00 00 00 90 00 a0 a4 00 00 02 a4
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
^CBye
%

View File

@@ -1,29 +0,0 @@
// ONLY sample code:
Write msg
-I- pAPDU[0]=0x0
-I- pAPDU[1]=0xA4
-I- pAPDU[2]=0x0
-I- pAPDU[3]=0x0
-I- pAPDU[4]=0x2
-I- pAPDU[5]=0x3F
-I- wlength=7
-I- CASE=0x3 NeNc=0x2
-I- procByte: 0xA4
-I- HdlINS
-I- SW1=0x6E, SW2=0x0
// SIMTRACE:
-I- typ=0x6F
-I- APDU cmd: a0 a4 0 ..-I- pAPDU[0]=0xA0
-I- pAPDU[1]=0xA4
-I- pAPDU[2]=0x0
-I- pAPDU[3]=0x0
-I- pAPDU[4]=0x2
-I- pAPDU[5]=0x3F
-I- wlength=7
-I- CASE=0x3 NeNc=0x2
-I- procByte: 0xA4
-I- HdlINS
-I- SW1=0x94, SW2=0x4

View File

@@ -1,51 +0,0 @@
#!/usr/bin/env python
import argparse
import sniffer
import ccid
import ccid_select
import mitm
import usb.core
import usb.util
import sys
import time
def find_dev():
dev = usb.core.find(idVendor=0x1d50, idProduct=0x60e3)
if dev is None:
raise ValueError("Device not found")
else:
print("Found device")
return dev
# main code
def main():
parser = argparse.ArgumentParser()
# FIXME: config names instead of numbers
parser.add_argument("-C", "--conf", type=int, choices=[1, 2, 3, 4], help="Set USB config")
parser.add_argument("-s", "--sniff", help="Sniff communication!", action='store_true')
parser.add_argument("-S", "--select_file", help="Transmit SELECT cmd!", action='store_true')
parser.add_argument("-p", "--phone", help="Emulates simcard", action='store_true')
parser.add_argument("-m", "--mitm", help="Intercept communication (MITM)", action='store_true')
args = parser.parse_args()
print("args: ", args)
dev = find_dev()
if args.conf is not None:
dev.set_configuration(args.conf)
# Give pcsclite time to find the device
time.sleep(1)
if args.sniff is True:
sniffer.sniff(dev)
# FIXME: This command is test code and should be removed in near future:
if args.select_file is True:
ccid_select.select()
if args.phone is True:
mitm.do_mitm(dev, sim_emul=True)
if args.mitm is True:
mitm.do_mitm(dev, sim_emul=False)
return
main()

View File

@@ -1,99 +0,0 @@
chrysh@gearbox ..a_thesis/sysmocom_repo/usb_application (git)-[master] % ./simtrace.py -b :(
('args: ', Namespace(cmd=None, conf=None, read_bin=True, select_file=False, sniff=False))
pcsc: wait_for_card
Reading ...
pdu: a0b000000a
ICCID: 8949901701053518711
pdu: a0b0000009
IMSI: 901702391248819
SMSP: ffffffffffffffffffffffffe1ffffffffffffffffffffffff058100945555ffffffffffff000000
pdu: a0b0000002
ACC: ffff
MSISDN: Not available
Done !
chrysh@gearbox ..a_thesis/sysmocom_repo/usb_application (git)-[master] % ./simtrace.py -S
('args: ', Namespace(cmd=None, conf=None, read_bin=False, select_file=True, sniff=False))
Context established!
PCSC Readers: ['Gemalto USB Shell Token V2 00 00']
Using reader: Gemalto USB Shell Token V2 00 00
Connected with active protocol 1
Select: 0x6E 0x00
Command: 0x6E 0x00
Disconnected
Released context.
BLAUE SIM:
chrysh@gearbox ..a_thesis/sysmocom_repo/usb_application (git)-[master] % ./simtrace.py -b
('args: ', Namespace(cmd=None, conf=None, read_bin=True, select_file=False, sniff=False))
pcsc: wait_for_card
Reading ...
pdu: a0b000000a
ICCID: 8988211000000004534
pdu: a0b0000009
IMSI: Can't read, response code = 9804
SMSP: Can't read, response code = 9804
pdu: a0b0000042
HPLMN: Can't read, response code = 9804
pdu: a0b0000002
ACC: Can't read, response code = 9804
MSISDN: Can't read, response code = 9804
Done !
chrysh@gearbox ..a_thesis/sysmocom_repo/usb_application (git)-[master] % ./simtrace.py -S
('args: ', Namespace(cmd=None, conf=None, read_bin=False, select_file=True, sniff=False))
Context established!
PCSC Readers: ['Gemalto USB Shell Token V2 00 00']
Using reader: Gemalto USB Shell Token V2 00 00
Connected with active protocol 1
Select: 0x6E 0x00
Command: 0x6D 0x00
Disconnected
Released context.
WEISSE SIM:
chrysh@gearbox ..a_thesis/sysmocom_repo/usb_application (git)-[master] % ./simtrace.py -S
('args: ', Namespace(cmd=None, conf=None, read_bin=False, select_file=True, sniff=False))
Context established!
PCSC Readers: ['Gemalto USB Shell Token V2 01 00']
Using reader: Gemalto USB Shell Token V2 01 00
Connected with active protocol 1
Select: 0x6E 0x00
Command: 0x6E 0x00
Disconnected
Released context.
chrysh@gearbox ..a_thesis/sysmocom_repo/usb_application (git)-[master] % ./simtrace.py -b
('args: ', Namespace(cmd=None, conf=None, read_bin=True, select_file=False, sniff=False))
pcsc: wait_for_card
Reading ...
pdu: a0b000000a
ICCID: 8949901701053518711
pdu: a0b0000009
IMSI: 901702391248819
ßSMSP: ffffffffffffffffffffffffe1ffffffffffffffffffffffff058100945555ffffffffffff000000
pdu: a0b000002d
HPLMN: 09f107ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
('Unexpected error:', <type 'exceptions.NameError'>)
pdu: a0b0000002
ACC: ffff
MSISDN: Not available
Done !
WEISE SIM mit SELECT vom file:
chrysh@gearbox ..a_thesis/sysmocom_repo/usb_application (git)-[master] % ./simtrace.py -S
('args: ', Namespace(cmd=None, conf=None, read_bin=False, select_file=True, sniff=False))
Context established!
PCSC Readers: ['Gemalto USB Shell Token V2 01 00']
Using reader: Gemalto USB Shell Token V2 01 00
Connected with active protocol 1
Select: 0x9F 0x16
Command: 0x6E 0x00
Disconnected
Released context.

View File

@@ -1,42 +0,0 @@
#!/usr/bin/env python
import constants
import array
INS = 1
LEN = 4
class SmartCardEmulator:
def getATR(self):
return array.array('B', constants.ATR_SYSMOCOM1)
def send_receive_cmd(self, cmd):
if cmd[INS] == 0xA4:
resp = [0x9F, 0x16]
elif cmd == [0xff, 0x00, 0xff]:
resp = cmd
elif len(cmd) == 5 and cmd[INS] == 0xC0:
data = self.ans_from_len[cmd[LEN]]
SW = [0x90, 0x00]
resp = data + SW # Respond with INS byte
#state = WAIT_RST
else:
print("Unknown cmd")
resp = [0x60, 0x00]
print("Cmd, resp: ")
print("".join("%02x " % b for b in cmd))
print("".join("%02x " % b for b in resp))
return array.array('B', resp)
def reset_card():
pass
def close(self):
pass
ans_from_len = {0x16: [0x00, 0x00, 0x00, 0x00, 0x7F, 0x20, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x09, 0x91, 0x00, 0x17,
0x04, 0x00, 0x83, 0x8A, 0x83, 0x8A],
}

View File

@@ -1,39 +0,0 @@
#!/usr/bin/env python3
import usb.core
import usb.util
import sys
import array
from apdu_split import Apdu_splitter, apdu_states
from gsmtap import gsmtap_send_apdu
from constants import PHONE_RD, ERR_TIMEOUT, ERR_NO_SUCH_DEV
# main code
def sniff(dev):
ans = array.array('B', [])
apdus = []
apdu = Apdu_splitter()
while True:
try:
ans += dev.read(PHONE_RD, 64, 1000)
except KeyboardInterrupt:
print("Bye")
sys.exit()
except Exception as e:
if e.errno != ERR_TIMEOUT and e.errno != ERR_NO_SUCH_DEV:
raise
print e
if len(ans) >= 1:
# print("".join("%02x " % b for b in ans))
for c in ans:
apdu.split(c)
if apdu.state == apdu_states.APDU_S_FIN:
apdus.append(apdu)
gsmtap_send_apdu(apdu.buf)
apdu = Apdu_splitter()
ans = array.array('B', [])

View File

@@ -1,39 +0,0 @@
import usb.core
import usb.util
import sys
dev = usb.core.find(idVendor=0x03eb, idProduct=0x6004)
if dev is None:
raise ValueError("Device not found")
else:
print("Found device")
dev.set_configuration()
cfg = dev.get_active_configuration()
print("Active config: ")
print(cfg)
print("NumConfigs: " + str(dev.bNumConfigurations))
#print(cfg.configurations)
print("***")
for cfg in dev:
print("*** Next configs: ")
print(cfg)
print("~~~~~~")
#cfg = usb.util.find_descriptor(dev, bConfiguration=0)
# nur config(1) funktioniert
# config(0): Device haengt
# config(2): usb.core.USBError: [Errno 2] Entity not found
print("dev.set_configuration(" + sys.argv[1] + ")")
dev.set_configuration(int(sys.argv[1]))
print("*** New config: ")
cfg = dev.get_active_configuration()
print(cfg)

View File

@@ -1,7 +0,0 @@
def HEX(vals):
if vals is not None:
if type(vals) is int:
return "%.2x"%vals
return ' '.join('%.2x'%x for x in vals)
return ''