23 Commits

Author SHA1 Message Date
a8e9074faf Add switch pin for ST-Dongle 2022-07-30 12:35:28 +02:00
NIIBE Yutaka
e12a7e0bb3 Version 1.21.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2022-04-22 11:11:05 +09:00
NIIBE Yutaka
fdbe91600d Backport struct qh changes from master.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2022-04-21 13:01:30 +09:00
NIIBE Yutaka
daca396027 Backport struct chx_qh changes from master.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2022-04-08 19:50:21 +09:00
NIIBE Yutaka
a6b96fe434 Version 1.20.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-12 11:19:41 +09:00
NIIBE Yutaka
c5a83cb9a5 usbip: Fix the value of URB_DATA_SIZE again.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-11 10:22:49 +09:00
NIIBE Yutaka
8f20122e54 more fix for libccid.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-04 13:56:32 +09:00
NIIBE Yutaka
c31a91947d Fix USB emulation driver for GNU/Linux.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-04 10:09:15 +09:00
NIIBE Yutaka
71cc5a8f32 Version 1.19.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-18 11:02:06 +09:00
NIIBE Yutaka
73d6c13d15 Backport ADC driver from master.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-17 16:27:17 +09:00
NIIBE Yutaka
d43fbb140a Backport AckBtn driver and USBIP driver for GNU/Linux from master.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-17 10:51:12 +09:00
NIIBE Yutaka
9b822282f0 GNU/Linux: Backport from 2.1.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-16 14:53:24 +09:00
NIIBE Yutaka
91ddc8fd02 GNU/Linux: Clear TP.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-12 09:07:06 +09:00
NIIBE Yutaka
0698bc4c9e Revert "Fix EP0 receiving more packets."
This reverts commit e58e134b42.

--

The fix was no-op, because following code does not use LEN at all.
For other machines, it uses LEN for hardware USB core.
2020-01-06 09:55:13 +09:00
NIIBE Yutaka
7dda49eb40 Revert "Add a ChangeLog entry for USB fix."
This reverts commit 4c0c15588e.
2020-01-06 09:54:22 +09:00
NIIBE Yutaka
cc49f4ef23 Version 1.18.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-30 10:43:35 +09:00
NIIBE Yutaka
a0732c125a Add ChangeLog entry.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 10:20:27 +09:00
NIIBE Yutaka
6be482413c chopstx_poll: More change.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 10:20:07 +09:00
NIIBE Yutaka
9253777f5f Fix chopstx_poll for condition variables, check after woken up.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 10:19:50 +09:00
NIIBE Yutaka
63f47cede3 example-cdc,etc.: Bug fix of examples.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-20 14:39:38 +09:00
NIIBE Yutaka
4c0c15588e Add a ChangeLog entry for USB fix.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-20 08:27:33 +09:00
NIIBE Yutaka
e58e134b42 Fix EP0 receiving more packets.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-20 08:26:49 +09:00
NIIBE Yutaka
4cd47453ae Fix GNU/Linux emulation about termination of a thread.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-04 13:44:57 +09:00
64 changed files with 1426 additions and 8543 deletions

44
AUTHORS
View File

@@ -34,56 +34,42 @@ NIIBE Yutaka:
chopstx.c, chopstx.h,
chopstx-cortex-m.c, chopstx-cortex-m.h,
chopstx-gnu-linux.c, chopstx-gnu-linux.h,
chopstx-riscv32.c, chopstx-riscv32.h,
entry.c, entry-cortex-m.c, entry-riscv32.c,
entry.c,
eventflag.c, eventflag.h
Wrote the drivers under mcu/:
chx-gd32vf103.c, chx-gnu-linux.c, chx-mkl27z.c,
chx-stm32f0.c, chx-stm32f103.c, chx-stm32l4.c,
clk_gpio_init-gd32vf103.c, clk_gpio_init-mkl27z.c,
clk_gpio_init-stm32f.c, clk_gpio_init-stm32l.c
cortex-m.h, gd32vf103.h, mkl27z.h, stm32.h,
stm32f103.h, stm32l.h,
sys-gnu-linux.c, sys-gnu-linux.h,
Wrote the drivers mcu/*:
chx-gnu-linux.c, chx-mkl27z.c, chx-stm32f0.c, chx-stm32f103.c,
clk_gpio_init-mkl27z.c, clk_gpio_init-stm32f.c,
cortex-m.h, mkl27z.h, stm32.h, stm32f103.h,
sys-gnu-linux.c,sys-gnu-linux.h,
sys-mkl27z.c, sys-mkl27z.h,
sys-stm32f0.c, sys-stm32f0.h
sys-stm32f103.c, sys-stm32f103.h,
sys-stm32l4.c, sys-stm32l4.h,
usb-gd32vf103.c, usb-mkl27z.c,
usb-st-common.c, usb-stm32f103.c, usb-stm32l4.c
usb-stm32f103.c, usb-mkl27z.c
Wrote the drivers:
contrib/adc-mkl27z.c,
contrib/spi.h, contrib/spi-st.c
controb/adc-mkl27z.c
Drew the logo:
chopstx.svg, chopstx.png
Wrote examples:
example-led, example-cdc, example-fsm-55, example-fs-bb48,
example-usb-serial, example-cdc-gnu-linux,
example-usart, example-lcd
example-usb-serial, example-cdc-gnu-linux
Wrote board/*:
board-gnu-linux.h,
board-fst-01sz.h,
board-fst-01g.h, board-fst-01.h, board-fst-01-00.h,
board-olimex-stm32-h103.h, board-stm8s-discovery.h
board-cq-starm.h, board-stbee-mini.h, board-stbee.h,
board-stm32f0-discovery.h, board-fsm-55.h,
board-st-nucleo-l432.h,
board-fs-bb48.h,
board-blue-pill-g.h,
board-longan-nano.h
board-fs-bb48.h
For Free Software Initiative of Japan, wrote:
contrib/adc-stm32f103.c,
contrib/adc-gnu-linux.c
Under contract of g10 Code GmbH, wrote:
mcu/usb-usbip.c,
contrib/ackbtn.h, contrib/ackbtn-stm32f103.c,
contrib/usart.h,
contrib/usart-common.c, contrib/usart-common-f103.c,
contrib/usart-gd32vf103.c,
contrib/usart-impl-f103.h, contrib/usart-impl.h,
contrib/usart-stm32f103.c, contrib/usart-stm32l4.c,
mcu/usb-usbip.c
contrib/usart-stm32f103.c
contrib/ackbtn-stm32f103.c
Paul Fertser:
Added Blue Pill support.

195
ChangeLog
View File

@@ -1,27 +1,77 @@
2020-06-26 NIIBE Yutaka <gniibe@fsij.org>
2022-04-22 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 2.0
* doc/chopstx.texi (VERSION): 2.0.
* VERSION: 1.21.
* doc/chopstx.texi (VERSION): 1.21.
2020-06-23 NIIBE Yutaka <gniibe@fsij.org>
2022-04-21 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.c (chopstx_critical): New.
Backport the changes from master.
* chopstx.c (struct chx_pq): Use struct qh.
(struct chx_px): Use struct qh.
(struct chx_thread): Use struct qh.
(FOR_QUEUE): Remove.
(ll_dequeue): Follow the changes of above.
(ll_prio_push): Use struct qh for the loop.
(ll_prio_enqueue): Likewise.
(chx_set_timer): Use struct qh for the first argument.
(chx_timer_insert, chx_timer_dequeue): Use struct qh for the loop.
(chx_timer_expired): Likewise.
(chx_init, chopstx_create, chx_proxy_init): Use the address at q.
(chx_exit): Use struct qh for the loop.
(chx_mutex_unlock): Fix the calculation of the priority.
* chopstx-cortex-m.c (chx_handle_intr): Not use FOR_QUEUE macro.
* chopstx-gnu-linux.c (chx_handle_intr): Not use FOR_QUEUE macro.
2019-12-27 NIIBE Yutaka <gniibe@fsij.org>
2022-04-08 NIIBE Yutaka <gniibe@fsij.org>
* contrib/spi-st.c (check_transmit, put_data): Fix handling
conditions.
[!SPI_USE_INTERRUPT]: Support busy wait mode.
Backport the changes from master.
* chopstx.h (struct chx_qh): Change the type for next and prev.
* chopstx.c (FOR_QUEUE): New macro.
(ll_empty): Follow the change of struct chx_qh.
(ll_insert): Change the type of the first argument.
(ll_pop): Follow the change of struct chx_qh.
(ll_prio_push): Use FOR_QUEUE macro. Follow the change of
ll_insert.
(ll_prio_enqueue, chx_timer_insert): Likewise.
(chx_timer_dequeue, chx_init, chx_exit): Likewise.
(chopstx_mutex_init, chopstx_cond_init): Likewise.
* chopstx-cortex-m.c (chx_handle_intr): Use FOR_QUEUE macro.
* chopstx-gnu-linux.c (chx_handle_intr): Use FOR_QUEUE macro.
2019-12-27 NIIBE Yutaka <gniibe@fsij.org>
2021-10-12 NIIBE Yutaka <gniibe@fsij.org>
* mcu/usb-gd32vf103.c: New.
* VERSION: 1.20.
* doc/chopstx.texi (VERSION): 1.20.
* mcu/clk_gpio_init-gd32vf103.c (clock_init): Configure USB clock.
2021-10-11 NIIBE Yutaka <gniibe@fsij.org>
* mcu/gd32vf103.h (RCU_CFG0_USBFSPSC_DIV2): New.
Backport from master.
* mcu/usb-usbip.c (URB_DATA_SIZE): Tweak the value.
* usb_lld.h [MCU_GD32VF1]: Support GD32VF103.
2021-02-18 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.19.
* doc/chopstx.texi (VERSION): 1.19.
2021-02-17 NIIBE Yutaka <gniibe@fsij.org>
Backport from master.
* contrib/ackbtn-gnu-linux.c: New.
* mcu/usb-usbip.c (usbip_run_server): Add start-up message.
* contrib/adc-gnu-linux.c (adc_start_conversion): Use getrandom.
2021-02-12 NIIBE Yutaka <gniibe@fsij.org>
Backport from 2.1.
* chopstx-gnu-linux.c (chopstx_create_arch): Clear TP.
* example-cdc-gnu-linux/usb-cdc.c (tty_recv): Cancel the input.
* example-cdc-gnu-linux/sample.c (main): Handle timeout by canceling
input.
2019-12-30 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.18.
* doc/chopstx.texi (VERSION): 1.18.
2019-12-27 NIIBE Yutaka <gniibe@fsij.org>
@@ -32,123 +82,6 @@
* chopstx-gnu-linux.c (chx_thread_start): Fix return value.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* rules.mk [ARCH=riscv32] (MCFLAGS, LDFLAGS): Define.
(CFLAGS, LDFLAGS): Don't add -m for ARM specific.
* example-lcd: New.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* contrib/usart-gd32vf103.c: New.
* contrib/usart-common.c (usart_stat, get_usart_dev)
(get_usart_rb_h2a, get_usart_rb_a2h, get_usart_intr): Handle
when USART_DEVNO_START==0.
* contrib/usart-stm32f103.c: Use usart-impl.h.
Factor out to...
* contrib/usart-common-f103.c: New.
* contrib/usart-stm32l4.c: Factor out definitions into...
* contrib/usart-impl.h: New.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* board/board-longan-nano.h: New.
* mcu/gd32vf103.h: New.
* mcu/chx-gd32vf103.c: New.
* mcu/clk_gpio_init-gd32vf103.c: New.
* chopstx-riscv32.h: New.
* chopstx-riscv32.c: New.
* entry-riscv32.c: New.
* entry-cortex-m.c: Rename from entry.c.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* contrib/spi.h: New.
* contrib/spi-st.c: New.
* chopstx.c (chx_timer_expired): When no timer, explicitly call
chx_systick_reload with 0.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* example-cdc/Makefile (ARCH): Define.
* example-cdc-gnu-linux/Makefile (ARCH): Ditto.
* example-fraucheky/Makefile (ARCH): Ditto.
* example-fs-bb48/Makefile (ARCH): Ditto.
* example-fsm-55/Makefile (ARCH): Ditto.
* example-led/Makefile (ARCH): Ditto.
* example-primer2/Makefile (ARCH): Ditto.
* example-usart/Makefile (ARCH): Ditto.
* example-usb-serial/Makefile (ARCH): Ditto.
* chopstx.c: Include files by ARCH_HEADER and ARCH_IMPL.
* rules.mk (DEFS): Define ARCH_HEADER and ARCH_IMPL by ARCH.
2019-11-25 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (chx_idle): Synchronous IDLE.
(chx_idle, idle_stack): Remove.
(chx_init_arch): Don't makecontext for IDLE.
(preempted_context_switch): Simplify, because no case for
interrupting IDLE.
(voluntary_context_switch): Simply call chx_idle to get tp_next.
2019-11-22 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (preempted_context_switch): Rename.
2019-11-21 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.c (chx_sched): New common function.
* chopstx-cortex-m.c (svc): Simply context switch.
(voluntary_context_switch): New, replacing chx_sched.
* chopstx-gnu-linux.c (voluntary_context_switch): Likewise.
2019-11-21 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.h (CHOPSTX_PRIO_INHIBIT_PREEMPTION): Remove.
* chopstx.c (chx_init): Remove support of
CHOPSTX_PRIO_INHIBIT_PREEMPTION.
(chopstx_setpriority): Likewise.
* chopstx-cortex-m.c (CPU_EXCEPTION_PRIORITY_PENDSV): Change the
value. Now PendSV exception runs at same priority level of
all other exceptions (systick and interrupt exception).
(chx_cpu_sched_lock, chx_cpu_sched_unlock):
Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION.
(chx_request_preemption): Change the argument with none.
(chx_timer_handler): New. Use common function of
chx_timer_expired, local one, chx_request_preemption.
(chx_handle_intr): Use chx_recv_irq.
(chx_sched): Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION.
(preempt): Have an argument from tail-chaining.
Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION.
* chopstx-gnu-linux.c (chx_idle): Rename.
(chx_init_arch): Follow the rename.
(chx_handle_intr): Use chx_recv_irq, chx_running_preempted, and
chx_preempt_into.
(sigalrm_handler): Use chx_timer_expired, chx_running_preempted,
and chx_preempt_into.
(chx_preempt_into): Rename from chx_request_preemption, changing
argument. Remove the handling of preempted thread, which should
be done by caller using chx_running_preempted, beforehand.
* entry.c (vector_table): Use chx_timer_handler.
* chopstx.c (chx_timer_expired): Pop from the ready queue and
return the next thread to switch.
(chx_recv_irq): New.
(chx_running_preempted): New.
2019-11-21 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-cortex-m.c (chx_set_running): New.

48
NEWS
View File

@@ -1,25 +1,43 @@
NEWS - Noteworthy changes
* Major changes in Chopstx 2.0
* Major changes in Chopstx 1.21
Released 2020-06-26
Released 2022-04-22
** Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION
We used to have (an experimental) feature of higher priority of thread
(>= CHOPSTX_PRIO_INHIBIT_PREEMPTION). The feature offered: When a
thread has such a priority, Chostx guarantees that it keeps running
and no other threads can preempt the thread. I had introduced this
(questionable) feature as an excuse against no support of
masking/unmasking interrupt API. From the experience of Chopstx, it
is rather consistent to remove this feature. It was over engineering
to support a thread having such a special priority.
** Fix of struct chx_qh and struct chx_thread/pq/px.
** New function: chopstx_critical
Let run FUNC with ARG, under CPU scheduler lock (== no preemption).
Good compiler complains/warns for possible different size of
structures. To avoid this, access to the objects are fixed.
** RISC-V MCU support
RISC-V support has been added. It's for the core named Bumblebee.
* Major changes in Chopstx 1.20
Released 2021-10-12
** Fix of USB driver for GNU/Linux emulation
Fix the value of URB_DATA_SIZE, so that Gnuk can work with PC/SC.
* Major changes in Chopstx 1.19
Released 2021-02-18
** Acknowledge button support for GNU/Linux emulation
User is asked acknowledge by RET with standard input.
** USB driver for GNU/Linux emulation
It shows start-up message now.
** ADC driver for GNU/Linux emulation
It uses getrandom(2), output is not deterministic any more.
** Bug fix for GNU/Linux emulation
When creating a thread, allocated memory for struct chx_thread was not
initialized by chopstx_create_arch, assuming getcontext does it well.
To make sure (getcontext would not update the area for FPU registers,
when not used), it's now initialized by zeros.
* Major changes in Chopstx 1.18

16
README
View File

@@ -1,6 +1,6 @@
Chopstx - Threads and only Threads
Version 2.0
2020-06-26
Version 1.21
2021-04-22
Niibe Yutaka
Flying Stone Technology
@@ -9,8 +9,7 @@ What's Chopstx?
Chopstx is an RT thread library for STM32F103 and GD32F103 (ARM
Cortex-M3), STM32F030 (ARM Cortex-M0), MKL27Z (ARM Cortex-M0plus),
STM32L432 (ARM Cortex-M4), GD32V103 (RISC-V Bumblebee) and emulation
on GNU/Linux.
STM32L432 (ARM Cortex-M4), and emulation on GNU/Linux.
While most RTOSes come with many features, drivers, and protocol
stacks, Chopstx just offers a simple RT thread library.
@@ -61,11 +60,6 @@ For STM32 Primer2, see the directory: example-primer2.
Future Works
============
Convenience function to determine the bottom of thread stack,
configuration of thread size by compiler's output would be next things
to be done.
Experimental SMP port for Cortex-A7 is under development. For SMP,
more careful considerations for shared access to objects of struct
chx_pq is needed. So, modifications required will not be small.
RISC-V port (for GD32VF103) is available in 2.x. Please have a look
at the master branch, and test it if possible.
--

View File

@@ -1 +1 @@
release/2.0
release/1.21

View File

@@ -1,75 +0,0 @@
#define BOARD_NAME "Longan-nano"
#define BOARD_ID 0xe65dace7
/* echo -n "Longan-nano" | sha256sum | sed -e 's/^.*\(........\) -$/\1/' */
#define MCU_CORE_BUMBLEBEE 1
#define MCU_GD32VF1 1
/*
* XTAL clock = 8 MHz
* Core clock = 96 MHz
* AHB clock = 96 MHz
* APB1 clock = 48 MHz
* APB2 clock = 96 MHz
* USB clock = 48 MHz
* ADC clock = ???
*/
/* HXTAL 8MHz input => PLL output = 8 * 12 = 96 MHz */
#define RCU_CFG0_PLL_MUL_VALUE RCU_CFG0_PLL_MUL12
#define GPIO_LED GPIOC
#define GPIO_LED_CLEAR_TO_EMIT 13 /* LED Red */
#define GPIO_USB GPIOA
#define GPIO_OTHER GPIOB
/*
* Port A setup.
*
* PA1 - Push-pull output 2MHz 1 default (LED green 0: ON 1: OFF)
* PA2 - Push-pull output 2MHz 1 default (LED blue 0: ON 1: OFF)
* PA3 -
* PA4 - <Can be used for GPIO>
* PA5 - AF output push-pull 50MHz: SPI0_SCK
* PA6 - Input pull-up: SPI0_MISO (master)
* PA7 - AF output push-pull 50MHz: SPI0_MOSI (master)
* PA8 - (USBFS_SOF)
* PA9 - AF output push-pull 2MHz: USART0_TX (USBFS_VBUS)
* PA10 - Input pull-up: USART0_RX0 (USBFS_ID)
* PA11 - Push Pull output 10MHz 0 default (until USB enabled???) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled???) (USBDP)
* ------------------------ Default
* PAx - input with pull-up.
*
* NOTE: It's USB device bus power configuration, where we don't
* monitor VBUS voltage change. For self powered system, we need to
* monitor VBUS pin for detecting USB connection. Supporting self
* powered system, you need to change the USB driver, and when doing
* so, PA9 should be used for USBFS_VBUS.
*/
#define VAL_GPIO_USB_ODR 0xFFFFE6FF
#define VAL_GPIO_USB_CRL 0xB8B88228 /* PA7...PA0 */
#define VAL_GPIO_USB_CRH 0x888118A8 /* PA15...PA8 */
#define RCU_APB2_GPIO (RCU_APB2_GPIOA|RCU_APB2_GPIOB|RCU_APB2_GPIOC)
/*
* PB0 - Push-pull output 50MHz 1 default (OLED D/C#)
* PB1 - Push-pull output 10MHz 1 default (OLED RST#)
* PB2 - Push-pull output 50MHz 1 default (OLED CS#)
*/
#define VAL_GPIO_OTHER_ODR 0xFFFFFFFF
#define VAL_GPIO_OTHER_CRL 0x88888313 /* PB7...PB0 */
#define VAL_GPIO_OTHER_CRH 0x88888888 /* PB15...PB8 */
/*
* PC13 - Push-pull output 2MHz 1 default (LED red 0: ON 1: OFF)
*/
#define VAL_GPIO_LED_ODR 0xFFFFFFFF
#define VAL_GPIO_LED_CRL 0x88888888 /* PC7...PC0 */
#define VAL_GPIO_LED_CRH 0x88288888 /* PC15...PC8 */
/*
* Board specific information other than clock and GPIO initial
* setting should not be in board-*.h, but each driver should include
* such specific information by itself.
*/

View File

@@ -76,15 +76,17 @@ struct chx_stack_regs {
* ---------------------
* Prio 0x40: thread temporarily inhibiting schedule for critical region
* ...
* Prio 0xb0: systick, external interrupt, pendsv
* Prio 0xb0: systick, external interrupt
* Prio 0xc0: pendsv
* =====================================
*
* Cortex-M0
* =====================================
* Prio 0x00: thread temporarily inhibiting schedule for critical region
* ...
* Prio 0x40: systick, external interrupt, pendsv
* Prio 0x80: svc (not used)
* Prio 0x40: systick, external interrupt
* Prio 0x80: pendsv
* Prio 0x80: svc
* =====================================
*/
@@ -93,18 +95,18 @@ struct chx_stack_regs {
#if defined(__ARM_ARCH_6M__)
#define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x00
/* ... */
#define CPU_EXCEPTION_PRIORITY_SYSTICK 0x40
#define CPU_EXCEPTION_PRIORITY_INTERRUPT CPU_EXCEPTION_PRIORITY_SYSTICK
#define CPU_EXCEPTION_PRIORITY_PENDSV CPU_EXCEPTION_PRIORITY_SYSTICK
#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT
#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0x40
#define CPU_EXCEPTION_PRIORITY_PENDSV 0x80
#define CPU_EXCEPTION_PRIORITY_SVC 0x80 /* No use in this arch */
#elif defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
#define CPU_EXCEPTION_PRIORITY_SVC 0x30
#define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x40
/* ... */
#define CPU_EXCEPTION_PRIORITY_SYSTICK 0xb0
#define CPU_EXCEPTION_PRIORITY_INTERRUPT CPU_EXCEPTION_PRIORITY_SYSTICK
#define CPU_EXCEPTION_PRIORITY_PENDSV CPU_EXCEPTION_PRIORITY_SYSTICK
#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT
#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0xb0
#define CPU_EXCEPTION_PRIORITY_PENDSV 0xc0
#else
#error "no support for this arch"
#endif
@@ -236,57 +238,59 @@ chx_interrupt_controller_init (void)
static void
chx_cpu_sched_lock (void)
{
if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION)
{
#if defined(__ARM_ARCH_6M__)
asm volatile ("cpsid i" : : : "memory");
asm volatile ("cpsid i" : : : "memory");
#else
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED;
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED;
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
#endif
}
}
static void
chx_cpu_sched_unlock (void)
{
if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION)
{
#if defined(__ARM_ARCH_6M__)
asm volatile ("cpsie i" : : : "memory");
asm volatile ("cpsie i" : : : "memory");
#else
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_CLEAR;
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_CLEAR;
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
#endif
}
}
static void
chx_request_preemption (void)
{
*ICSR = (1 << 28);
asm volatile ("" : : : "memory");
}
struct chx_thread *
chx_timer_handler (void)
{
struct chx_thread *tp_next;
tp_next = chx_timer_expired ();
if (tp_next)
chx_request_preemption ();
return tp_next;
}
struct chx_thread *
void
chx_handle_intr (void)
{
struct chx_qh *q;
register uint32_t irq_num;
struct chx_thread *tp_next;
asm volatile ("mrs %0, IPSR\n\t"
"sub %0, #16" /* Exception # - 16 = interrupt number. */
: "=r" (irq_num) : /* no input */ : "memory");
tp_next = chx_recv_irq (irq_num);
if (tp_next)
chx_request_preemption ();
return tp_next;
chx_disable_intr (irq_num);
chx_spin_lock (&q_intr.lock);
for (q = q_intr.q.next; q != &q_intr.q; q = q->next)
{
struct chx_pq *p = (struct chx_pq *)q;
if (p->v == irq_num)
{ /* should be one at most. */
struct chx_px *px = (struct chx_px *)p;
ll_dequeue (p);
chx_wakeup (p);
chx_request_preemption (px->master->prio);
break;
}
}
chx_spin_unlock (&q_intr.lock);
}
static void
@@ -296,26 +300,51 @@ chx_init_arch (struct chx_thread *tp)
chx_set_running (tp);
}
static uintptr_t
voluntary_context_switch (struct chx_thread *tp_next)
static void
chx_request_preemption (uint16_t prio)
{
register uintptr_t result asm ("r0");
if (running == NULL || (uint16_t)running->prio < prio)
{
*ICSR = (1 << 28);
asm volatile ("" : : : "memory");
}
}
/*
* chx_sched: switch to another thread.
*
* There are two cases:
* YIELD=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* YIELD=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*
* For Cortex-M0, this should be AAPCS-compliant function entry, so we
* put "noinline" attribute.
*
* AAPCS: ARM Architecture Procedure Call Standard
*
* Returns:
* >= 1 on wakeup by others, value means ticks remained for sleep.
* 0 on normal wakeup (timer expiration, lock acquirement).
* -1 on cancellation.
*/
static uintptr_t __attribute__ ((naked, noinline))
chx_sched (uint32_t yield)
{
register struct chx_thread *tp asm ("r0");
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
asm volatile (
"svc #0\n\t"
"add r1, r0, #16\n\t"
"ldr r0, [r1]" /* Get tp->v */
: "=r" (result) : "0" (tp_next): "memory");
"svc #0"
: "=r" (tp) : "0" (yield): "memory");
#else
register struct chx_thread *tp asm ("r1");
register uint32_t arg_yield asm ("r1");
/* Build stack data as if it were an exception entry.
* And set the stop top to has RUNNNING.
*/
/* Build stack data as if it were an exception entry. */
/*
* r0: RUNNING scratch
* r0: TP scratch
* r1: 0 scratch
* r2: 0 scratch
* r3: 0 scratch
@@ -324,36 +353,46 @@ voluntary_context_switch (struct chx_thread *tp_next)
* pc: return address (= .L_CONTEXT_SWITCH_FINISH)
* psr: INITIAL_XPSR scratch
*/
asm ("mov %0, lr\n\t"
asm ("mov r1, lr\n\t"
"ldr r2, =.L_CONTEXT_SWITCH_FINISH\n\t"
"mov r3, #128\n\t"
"lsl r3, #17\n\t"
"push {%0, r2, r3}\n\t"
"mov %0, #0\n\t"
"mov r2, %0\n\t"
"mov r3, %0\n\t"
"push {%0, r2, r3}\n\t"
"push {r1, r2, r3}\n\t"
"mov r1, #0\n\t"
"mov r2, r1\n\t"
"mov r3, r1\n\t"
"push {r1, r2, r3}\n\t"
"mov r1, r0\n\t"
"ldr r2, =running\n\t"
"ldr %0, [r2]\n\t"
"push {%0, r3}\n\t"
: "=r" (tp)
: /* no input */
"ldr r0, [r2]\n\t"
"push {r0, r3}"
: "=r" (tp), "=r" (arg_yield)
: "0" (yield)
: "r2", "r3", "memory");
/* Save registers onto CHX_THREAD struct. */
asm ("add r1, #20\n\t"
"stm r1!, {r4, r5, r6, r7}\n\t"
asm ("add r0, #20\n\t"
"stm r0!, {r4, r5, r6, r7}\n\t"
"mov r2, r8\n\t"
"mov r3, r9\n\t"
"mov r4, r10\n\t"
"mov r5, r11\n\t"
"mov r6, sp\n\t"
"stm r1!, {r2, r3, r4, r5, r6}\n\t"
"sub r1, #56"
"stm r0!, {r2, r3, r4, r5, r6}\n\t"
"sub r0, #56"
: /* no output */
: "r" (tp)
: "r2", "r3", "r4", "r5", "r6", "r7", "memory");
if (arg_yield)
{
if (tp->flag_sched_rr)
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
tp = chx_ready_pop ();
asm volatile (/* Now, r0 points to the thread to be switched. */
/* Put it to *running. */
"ldr r1, =running\n\t"
@@ -382,9 +421,15 @@ voluntary_context_switch (struct chx_thread *tp_next)
"ldm r0!, {r1, r2}\n\t"
"mov r11, r1\n\t"
"mov sp, r2\n\t"
"sub r0, #45\n\t"
"ldrb r1, [r0]\n\t" /* ->PRIO field. */
"cmp r1, #247\n\t"
"bhi 1f\n\t" /* Leave interrupt disabled if >= 248 */
/**/
/* Unmask interrupts. */
"cpsie i\n\t"
"cpsie i\n"
/**/
"1:\n\t"
/*
0: r0
4: r1
@@ -428,14 +473,17 @@ voluntary_context_switch (struct chx_thread *tp_next)
"pop {r0, r1, r2, r3}\n\t"
"add sp, #12\n\t"
"pop {pc}\n\t"
".L_CONTEXT_SWITCH_FINISH:\n\t"
"add r0, #16\n\t"
"ldr r0, [r0]" /* Get tp->v */
: "=r" (result) /* Return value in R0 */
: "0" (tp_next)
".L_CONTEXT_SWITCH_FINISH:"
: "=r" (tp) /* Return value in R0 */
: "0" (tp)
: "memory");
#endif
return (uintptr_t)result;
asm volatile ("bx lr"
: "=r" (tp)
: "0" (tp->v)
: "memory");
return (uintptr_t)tp;
}
extern void cause_link_time_error_unexpected_size_of_struct_chx_thread (void);
@@ -476,18 +524,26 @@ chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
*/
void __attribute__ ((naked))
preempt (struct chx_thread * tp_next)
preempt (void)
{
register struct chx_thread *tp_current asm ("r1");
register struct chx_thread *tp asm ("r0");
register struct chx_thread *cur asm ("r1");
asm ( "ldr r2, =running\n\t"
"ldr r1, [r2]"
: "=r" (tp_current)
: /* no input */
asm volatile (
#if defined(__ARM_ARCH_6M__)
"cpsid i\n\t"
#else
"msr BASEPRI, r0\n\t"
#endif
"ldr r2, =running\n\t"
"ldr r0, [r2]\n\t"
"mov r1, r0"
: "=r" (tp), "=r" (cur)
: "0" (CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED)
: "r2");
if (!tp_current)
/* It's idle thread. No need to save registers. */
if (!cur)
/* It's idle thread. It's ok to clobber registers. */
;
else
{
@@ -501,20 +557,38 @@ preempt (struct chx_thread * tp_next)
"mov r5, r11\n\t"
"mrs r6, PSP\n\t" /* r13(=SP) in user space. */
"stm %0!, {r2, r3, r4, r5, r6}"
: "=r" (tp_current)
: "0" (tp_current)
/*
: "=r" (cur)
: "0" (cur)
/*
* Memory clobber constraint here is not accurate, but this
* works. R7 keeps its value, but having "r7" here prevents
* use of R7 before this asm statement.
*/
: "r2", "r3", "r4", "r5", "r6", "r7", "memory");
tp_next = chx_running_preempted (tp_next);
if (tp)
{
if (tp->flag_sched_rr)
{
if (tp->state == THREAD_RUNNING)
{
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
/*
* It may be THREAD_READY after chx_timer_expired.
* Then, do nothing.
*/
}
else
chx_ready_push (tp);
}
}
/* Registers on stack (PSP): r0, r1, r2, r3, r12, lr, pc, xpsr */
tp = chx_ready_pop ();
asm volatile (
".L_CONTEXT_SWITCH:\n\t"
/* Now, r0 points to the thread to be switched. */
@@ -547,14 +621,20 @@ preempt (struct chx_thread * tp_next)
"ldr r1, [r0], #4\n\t"
"msr PSP, r1\n\t"
#endif
"sub r0, #45\n\t"
"ldrb r1, [r0]\n\t" /* ->PRIO field. */
"mov r0, #0\n\t"
"cmp r1, #247\n\t"
"bhi 0f\n\t" /* Leave interrupt disabled if >= 248 */
/**/
/* Unmask interrupts. */
#if defined(__ARM_ARCH_6M__)
"cpsie i\n\t"
"cpsie i\n"
#else
"msr BASEPRI, r0\n\t"
"msr BASEPRI, r0\n"
#endif
/**/
"0:\n\t"
"sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0\n"
"1:\n\t"
@@ -581,21 +661,27 @@ preempt (struct chx_thread * tp_next)
/**/
"sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0"
: /* no output */ : "r" (tp_next) : "memory");
: /* no output */ : "r" (tp) : "memory");
}
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
/*
* System call: switch to another thread.
* There are two cases:
* ORIG_R0=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* ORIG_R0=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*/
void __attribute__ ((naked))
svc (void)
{
register uint32_t tp_next asm ("r0");
register struct chx_thread *tp asm ("r0");
register uint32_t orig_r0 asm ("r1");
asm ("ldr r1, =running\n\t"
"ldr r1, [r1]\n\t"
"add r1, #20\n\t"
"ldr r0, [r1]\n\t"
"add r1, r0, #20\n\t"
/* Save registers onto CHX_THREAD struct. */
"stm r1!, {r4, r5, r6, r7}\n\t"
"mov r2, r8\n\t"
@@ -604,15 +690,23 @@ svc (void)
"mov r5, r11\n\t"
"mrs r6, PSP\n\t" /* r13(=SP) in user space. */
"stm r1!, {r2, r3, r4, r5, r6}\n\t"
"ldr r0, [r6]\n\t"
"sub r1, #56\n\t"
"str r1, [r6]"
: "=r" (tp_next)
"ldr r1, [r6]\n\t"
"str r0, [r6]"
: "=r" (tp), "=r" (orig_r0)
: /* no input */
: "r1", "r2", "r3", "r4", "r5", "r6", "memory");
: "r2", "r3", "r4", "r5", "r6", "memory");
if (orig_r0) /* yield */
{
if (tp->flag_sched_rr)
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
tp = chx_ready_pop ();
asm volatile (
"b .L_CONTEXT_SWITCH"
: /* no output */ : "r" (tp_next) : "memory");
: /* no output */ : "r" (tp) : "memory");
}
#endif

View File

@@ -2,7 +2,7 @@
* chopstx-gnu-linux.c - Threads and only threads: Arch specific code
* for GNU/Linux emulation
*
* Copyright (C) 2017, 2018, 2019 Flying Stone Technology
* Copyright (C) 2017, 2018, 2019, 2021 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
@@ -54,8 +54,6 @@ chx_dmb (void)
}
static void preempted_context_switch (struct chx_thread *tp_next);
static sigset_t ss_cur;
static void
@@ -146,44 +144,41 @@ chx_cpu_sched_unlock (void)
pthread_sigmask (SIG_SETMASK, &ss_cur, NULL);
}
/* NOTE: Called holding the cpu_sched_lock. */
static struct chx_thread *
chx_idle (void)
static void
idle (void)
{
struct chx_thread *tp_next = NULL;
while (tp_next == NULL)
{
int sig;
sigset_t set;
sigfillset (&set);
if (sigwait (&set, &sig))
continue;
if (sig == SIGALRM)
tp_next = chx_timer_expired ();
else
tp_next = chx_recv_irq (sig);
}
return tp_next;
for (;;)
pause ();
}
void
chx_handle_intr (uint32_t irq_num)
{
struct chx_thread *tp_next;
struct chx_qh *q;
tp_next = chx_recv_irq (irq_num);
if (!tp_next)
return;
chx_disable_intr (irq_num);
chx_spin_lock (&q_intr.lock);
for (q = q_intr.q.next; q != &q_intr.q; q = q->next)
{
struct chx_pq *p = (struct chx_pq *)q;
if (p->v == irq_num)
{ /* should be one at most. */
struct chx_px *px = (struct chx_px *)p;
tp_next = chx_running_preempted (tp_next);
preempted_context_switch (tp_next);
ll_dequeue (p);
chx_wakeup (p);
chx_spin_unlock (&q_intr.lock);
chx_request_preemption (px->master->prio);
return;
}
}
chx_spin_unlock (&q_intr.lock);
}
static ucontext_t idle_tc;
static char idle_stack[4096];
struct chx_thread main_thread;
void
@@ -201,17 +196,11 @@ chx_sigmask (ucontext_t *uc)
static void
sigalrm_handler (int sig, siginfo_t *siginfo, void *arg)
{
struct chx_thread *tp_next;
extern void chx_timer_expired (void);
ucontext_t *uc = arg;
(void)sig;
(void)siginfo;
tp_next = chx_timer_expired ();
if (tp_next)
{
tp_next = chx_running_preempted (tp_next);
preempted_context_switch (tp_next);
}
chx_timer_expired ();
chx_sigmask (uc);
}
@@ -227,47 +216,112 @@ chx_init_arch (struct chx_thread *tp)
sa.sa_flags = SA_SIGINFO|SA_RESTART;
sigaction (SIGALRM, &sa, NULL);
getcontext (&idle_tc);
idle_tc.uc_stack.ss_sp = idle_stack;
idle_tc.uc_stack.ss_size = sizeof (idle_stack);
idle_tc.uc_link = NULL;
makecontext (&idle_tc, idle, 0);
getcontext (&tp->tc);
chx_set_running (tp);
}
static void
preempted_context_switch (struct chx_thread *tp_next)
chx_request_preemption (uint16_t prio)
{
struct chx_thread *tp_prev = chx_running ();
ucontext_t *tcp;
struct chx_thread *tp_prev;
struct chx_thread *tp = chx_running ();
/*
* The swapcontext implementation may reset sigmask in the
* middle of its execution, unfortunately. It is best if
* sigmask restore is done at the end of the routine, but we
* can't assume that.
*
* Thus, there might be a race condition with regards to the
* user context TCP, if signal mask is cleared and signal comes
* in. To avoid this situation, we block signals.
*
* We don't need to fill the mask here. It keeps the condition
* of blocking signals before&after swapcontext call. It is
* done by the signal mask for sigaction, the initial creation
* of the thread, and the condition of chx_sched function which
* mandates holding cpu_sched_lock.
*/
chx_set_running (tp_next);
swapcontext (&tp_prev->tc, &tp_next->tc);
if (tp && (uint16_t)tp->prio >= prio)
return;
/* Change the context to another thread with higher priority. */
tp_prev = tp;
if (tp)
{
if (tp->flag_sched_rr)
{
if (tp->state == THREAD_RUNNING)
{
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
}
else
chx_ready_push (tp);
}
tp = chx_ready_pop ();
if (tp)
tcp = &tp->tc;
else
tcp = &idle_tc;
chx_set_running (tp);
if (tp_prev)
{
/*
* The swapcontext implementation may reset sigmask in the
* middle of its execution, unfortunately. It is best if
* sigmask restore is done at the end of the routine, but we
* can't assume that.
*
* Thus, there might be a race condition with regards to the
* user context TCP, if signal mask is cleared and signal comes
* in. To avoid this situation, we block signals.
*
* We don't need to fill the mask here. It keeps the condition
* of blocking signals before&after swapcontext call. It is
* done by the signal mask for sigaction, the initial creation
* of the thread, and the condition of chx_sched function which
* mandates holding cpu_sched_lock.
*/
swapcontext (&tp_prev->tc, tcp);
}
else if (tp)
{
setcontext (tcp);
}
}
/*
* chx_sched: switch to another thread.
*
* There are two cases:
* YIELD=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* YIELD=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*
* Returns:
* 1 on wakeup by others.
* 0 on normal wakeup.
* -1 on cancellation.
*/
static uintptr_t
voluntary_context_switch (struct chx_thread *tp_next)
chx_sched (uint32_t yield)
{
struct chx_thread *tp, *tp_prev;
ucontext_t *tcp;
if (!tp_next)
tp_next = chx_idle ();
tp = tp_prev = chx_running ();
if (yield)
{
if (tp->flag_sched_rr)
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
tp_prev = chx_running ();
chx_set_running (tp_next);
swapcontext (&tp_prev->tc, &tp_next->tc);
tp = chx_ready_pop ();
if (tp)
tcp = &tp->tc;
else
tcp = &idle_tc;
chx_set_running (tp);
swapcontext (&tp_prev->tc, tcp);
chx_cpu_sched_unlock ();
tp = chx_running ();
@@ -298,6 +352,7 @@ chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
* signal blocked. The sigmask will be cleared in chx_thread_start.
*/
chx_cpu_sched_lock ();
memset (tp, 0, sizeof (struct chx_thread));
getcontext (&tp->tc);
tp->tc.uc_stack.ss_sp = (void *)stack_addr;
tp->tc.uc_stack.ss_size = stack_size;

View File

@@ -1,684 +0,0 @@
/*
* chopstx-riscv32.c - Threads and only threads: Arch specific code
* for RISC-V 32 IMAC (Bumblebee core)
*
* Copyright (C) 2019
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
/*
* Define Bumblebee specific CSRs
*/
asm (
".equ msubm,0x7c4\n\t" /* No use (not needed to care about) */
".equ mtvt,0x307\n\t"
".equ mtvt2,0x7ec\n\t"
);
/* Data Memory Barrier. */
static void
chx_dmb (void)
{
asm volatile ("fence" : : : "memory");
}
/* No saved registers on the stack. */
/*
* Constants for RISC-V.
*/
#define REG_PC 0
#define REG_RA 1
#define REG_SP 2
#define REG_GP 3
#define REG_TP 4
#define REG_A0 10
#define MACHINE_STATUS_INIT 0x00001880 /* Machine-mode, interrupt enabled. */
/*
* We keep the TP register to the thread context address.
* Also, it is kept at the MSCRATCH register.
*/
static struct chx_thread *
chx_running (void)
{
uint32_t r;
asm ( "mv %0,tp" : "=r" (r));
if (r == 0)
return NULL;
return (struct chx_thread *)(r - 20);
}
static void
chx_set_running (struct chx_thread *tp)
{
asm ( "addi tp,%0,20\n\t"
"csrw mscratch,tp" : /* no output */ : "r" (tp) : "memory");
}
/*
* Lower layer architecture specific functions.
*
* system tick and interrupt
*/
/*
* System tick
*
* SYSTICK timer model is decreasing counter, where counter value 0
* means the timer stop.
*
* Since RISC-V MTIME is ever increasing counter, we need a bit of
* glue code here. Fortunately, we have MSTOP register for Bumblebee
* core, so, we can use the register to stop the counter. (If no such
* register stopping MTIME, we could put possible largest value in
* MTIMECMP, so that the timer won't be fired.)
*/
/* TIMER registers. */
struct TIMER {
volatile uint32_t mtime_lo;
volatile uint32_t mtime_hi;
volatile uint32_t mtimecmp_lo;
volatile uint32_t mtimecmp_hi;
uint32_t rsv0[1018];
volatile uint8_t mstop;
uint8_t rsv1[3];
volatile uint8_t msip;
uint8_t rsv2[3];
} __attribute__ ((packed));
static struct TIMER *const TIMER = (struct TIMER *)0xD1000000;
/* In Chopstx, we only use the lower 32-bit of the timer. */
static void
chx_systick_init_arch (void)
{
TIMER->mstop |= 1;
TIMER->mtime_hi = TIMER->mtime_lo = 0;
TIMER->mtimecmp_lo = 0xffffffff;
TIMER->mtimecmp_hi = 0;
}
/*
* @ticks: 0 means to stop the timer, otherwise update the timer
*/
static void
chx_systick_reload (uint32_t ticks)
{
if (!ticks)
{
TIMER->mstop |= 1;
TIMER->mtimecmp_lo = 0xffffffff;
}
else
{
TIMER->mtime_lo = 0;
TIMER->mtimecmp_lo = ticks;
if ((TIMER->mstop & 1))
TIMER->mstop &= ~1;
}
}
static uint32_t
chx_systick_get (void)
{
int stopped = (TIMER->mstop & 1);
if (stopped)
return 0;
else
{
uint32_t now = TIMER->mtime_lo;
if (TIMER->mtimecmp_lo <= now)
return 0;
return TIMER->mtimecmp_lo - now;
}
}
/* TIMER runs at 1/4 of core clock. */
static uint32_t
usec_to_ticks (uint32_t usec)
{
return usec * (MHZ/4);
}
static uint32_t
ticks_to_usec (uint32_t ticks)
{
return ticks / (MHZ/4);
}
/*
* Interrupt Handling of (E)CLIC
*/
/* CLIC registers. */
struct CLIC {
volatile uint8_t cfg;
uint8_t rsv[3];
volatile uint32_t info;
volatile uint8_t uth;
volatile uint8_t sth;
volatile uint8_t hth;
volatile uint8_t mth;
} __attribute__ ((packed));
static struct CLIC *const CLIC = (struct CLIC *)0xD2000000;
struct CLIC_INT {
volatile uint8_t ip;
volatile uint8_t ie;
volatile uint8_t attr;
volatile uint8_t ctl;
} __attribute__ ((packed));
static struct CLIC_INT *const CLIC_INT = (struct CLIC_INT *)0xD2001000;
static void
chx_enable_intr (uint8_t irq_num)
{
CLIC_INT[irq_num].ie |= 1;
}
static void
chx_clr_intr (uint8_t irq_num)
{
/* Clear pending interrupt is done automatically by the hardware. */
(void)irq_num;
}
static int
chx_disable_intr (uint8_t irq_num)
{
int already_disabled = !(CLIC_INT[irq_num].ie & 1);
CLIC_INT[irq_num].ie &= ~1;
return already_disabled;
}
static void
chx_set_intr_prio (uint8_t irq_num)
{
CLIC_INT[irq_num].attr = 0xc0; /* Level triggered, SHV=0 */
/* Note: SHV: Selective Hardware Vectoring: off (use common routine). */
CLIC_INT[irq_num].ctl = 0xff;
}
#define SWINT_IRQ 3
#define TIMER_IRQ 7
#define MEMERR_IRQ 17 /* Why on earth it's IRQ??? */
static void chx_handle_intr (void);
static void exception_handler (void);
/* Alignment to 64 is needed to enable ECLIC mode. */
static void __attribute__ ((naked,aligned(64)))
exception_handler (void)
{
asm volatile (
"0: j 0b"
: /* no output */);
}
static void __attribute__ ((naked))
memory_error (void)
{
asm volatile (
"0: j 0b"
: /* no output */);
}
typedef void (*handler)(void);
/* Not used (because we always disable SHV for all interrupts),
* Just in case, if some interrupt uses SHV=1.
*/
static const handler vector_table[] __attribute__ ((aligned(512))) = {
0, 0, 0, chx_handle_intr,
0, 0, 0, chx_handle_intr,
0, 0, 0, 0,
0, 0, 0, 0,
0, memory_error, 0, 0,
};
static void
chx_interrupt_controller_init (void)
{
asm volatile (
"csrw mtvt,%0"
: /* no output */ : "r" (vector_table) : "memory" );
asm volatile (
"csrw mtvt2,%0\n\t"
"csrsi mtvt2,1"
: /* no output */ : "r" (chx_handle_intr) : "memory");
asm volatile (
"csrw mtvec,%0\n\t"
"csrsi mtvec,3" /* Enable ECLC mode */
: /* no output */ : "r" (exception_handler) : "memory" );
CLIC->cfg &= 0xe1; /* NLBITS = 0 */
CLIC->mth = 0;
/* In Bumblebee core, timer interrupt is also handled by CLIC. */
chx_set_intr_prio (SWINT_IRQ);
chx_enable_intr (SWINT_IRQ);
chx_set_intr_prio (TIMER_IRQ);
chx_enable_intr (TIMER_IRQ);
chx_set_intr_prio (MEMERR_IRQ);
chx_enable_intr (MEMERR_IRQ);
}
/* Just for testing TIMER and ECLIC. No real use for now. */
static void
chx_sw_int (int go)
{
if (go)
TIMER->msip |= 1;
else
TIMER->msip &= ~1;
}
static void
chx_cpu_sched_lock (void)
{
asm volatile ("csrci mstatus,8" : : : "memory" );
}
static void
chx_cpu_sched_unlock (void)
{
asm volatile ("csrsi mstatus,8" : : : "memory" );
}
static void
chx_init_arch (struct chx_thread *tp)
{
memset (&tp->tc, 0, sizeof (tp->tc));
chx_set_running (tp);
}
#define SAVE_CALLEE_SAVE_REGISTERS \
"# Save callee save registers\n\t" \
"sw s0,32(sp)\n\t" \
"sw s1,36(sp)\n\t" \
"sw s2,72(sp)\n\t" \
"sw s3,76(sp)\n\t" \
"sw s4,80(sp)\n\t" \
"sw s5,84(sp)\n\t" \
"sw s6,88(sp)\n\t" \
"sw s7,92(sp)\n\t" \
"sw s8,96(sp)\n\t" \
"sw s9,100(sp)\n\t" \
"sw s10,104(sp)\n\t" \
"sw s11,108(sp)\n\t"
#define RESTORE_CALLEE_SAVE_REGISTERS \
"# Restore callee save registers\n\t" \
"lw s0,32(sp)\n\t" \
"lw s1,36(sp)\n\t" \
"lw s2,72(sp)\n\t" \
"lw s3,76(sp)\n\t" \
"lw s4,80(sp)\n\t" \
"lw s5,84(sp)\n\t" \
"lw s6,88(sp)\n\t" \
"lw s7,92(sp)\n\t" \
"lw s8,96(sp)\n\t" \
"lw s9,100(sp)\n\t" \
"lw s10,104(sp)\n\t" \
"lw s11,108(sp)\n\t"
#define SAVE_OTHER_REGISTERS \
"# Save other registers\n\t" \
"sw ra,4(sp)\n\t" \
"sw gp,12(sp)\n\t" \
"sw t0,20(sp)\n\t" \
"sw t1,24(sp)\n\t" \
"sw t2,28(sp)\n\t" \
"sw a0,40(sp)\n\t" \
"sw a1,44(sp)\n\t" \
"sw a2,48(sp)\n\t" \
"sw a3,52(sp)\n\t" \
"sw a4,56(sp)\n\t" \
"sw a5,60(sp)\n\t" \
"sw a6,64(sp)\n\t" \
"sw a7,68(sp)\n\t" \
"sw t3,112(sp)\n\t" \
"sw t4,116(sp)\n\t" \
"sw t5,120(sp)\n\t" \
"sw t6,124(sp)\n\t"
#define RESTORE_OTHER_REGISTERS \
"# Restore other registers\n\t" \
"lw ra,4(sp)\n\t" \
"lw gp,12(sp)\n\t" \
"lw t0,20(sp)\n\t" \
"lw t1,24(sp)\n\t" \
"lw t2,28(sp)\n\t" \
"lw a0,40(sp)\n\t" \
"lw a1,44(sp)\n\t" \
"lw a2,48(sp)\n\t" \
"lw a3,52(sp)\n\t" \
"lw a4,56(sp)\n\t" \
"lw a5,60(sp)\n\t" \
"lw a6,64(sp)\n\t" \
"lw a7,68(sp)\n\t" \
"lw t3,112(sp)\n\t" \
"lw t4,116(sp)\n\t" \
"lw t5,120(sp)\n\t" \
"lw t6,124(sp)\n\t"
/*
* MSTATUS register:
* 31: SD
* 16-15: XS (Extra Unit state)
* 14-13: FS (Floating-point Unit state)
* 12-11: MPP (Previous Privilege)
* 7: MPIE (Previous Interrupt Enable flag)
* 3: MIE (Interrupt Enable flag)
*/
/*
* MSUBM register:
* 9-8 PTYP (Previous Type-of-execution)
* 7-6 TYP (Currunt Type-of-execution)
* 0: Normal, 1: Interrupt, 2: Excep, 3: NMI
*
* No need to store PTYP field of MSUBM in MACHINE_STATUS.
* No need to setup MSUBM for PTYP.
* Since it's always 0 when this is called.
*
* Save MPP..MPIE in MACHINE_STATUS in the thread context.
*/
#define SETUP_MSTATUS_FROM_MACHINE_STATUS \
"lw a0,128(sp)\n\t" /* MPP..MPIE is in MACHINE_STATUS */ \
"csrr a1,mstatus\n\t" \
"srli a1,a1,13\n\t" \
"slli a1,a1,13\n\t" /* SD, XS, and FS bits from MSTATUS */ \
"or a0,a0,a1\n\t" \
"csrw mstatus,a0\n\t" /* Note: keep MIE=0 */
/* It is good if ISA has a single instruction for this operation. */
#define CATCH_AN_INTERRUPT_SYNCHRONOUSLY \
"mv a0,zero\n" \
"0:\n\t" \
/* \
* Interrupt is masked here, and it executes the WFI \
* instruction. When an interrupt occurs, the core is waken \
* up (even if it is masked). \
*/ \
"csrci mstatus,8\n\t" /* Interrupt should be masked already. */ \
/* Nevertheless, make sure it's masked. */ \
"wfi\n\t" \
/* \
* It is good if MCAUSE were updated here, but it is only \
* updated when the control goes into the interrupt service, \
* in a step of the interrupt handling steps of the core. So, \
* we let it go to chx_handle_intr, by unmasking. \
*/ \
"csrsi mstatus,8\n\t" /* Unmask interrupts to catch one. */ \
/* Just before this line, it is interrupted. */ \
/* And interrupt is masked and a0 is set. */ \
"beqz a0,0b\n\t" /* Just in case if not, loop. */
/*
* The idle function.
*
* NOTE: In this function, interrupt is masked (MIE=0) and interrupt
* is synchronously handled.
*/
static struct chx_thread * __attribute__ ((used))
chx_idle (void)
{
extern void chx_prepare_sleep_mode (void);
register struct chx_thread *tp_next asm ("a0") = NULL;
while (tp_next == NULL)
{
register uint32_t irq_num asm ("a0");
chx_prepare_sleep_mode (); /* MCU specific sleep setup */
asm volatile (
CATCH_AN_INTERRUPT_SYNCHRONOUSLY
/*
* In chx_handle_intr, a0 is set by the value of MCAUSE.
*/
"slli a0,a0,20\n\t"
"srli a0,a0,20" /* Take lower 12-bit of MCAUSE */
: "=r" (irq_num));
/* Note: here, interrupt is masked again. */
if (irq_num == SWINT_IRQ)
chx_sw_int (0);
else if (irq_num == TIMER_IRQ)
tp_next = chx_timer_expired ();
else if (irq_num == MEMERR_IRQ)
memory_error ();
else
tp_next = chx_recv_irq (irq_num);
}
return tp_next;
}
static uintptr_t
voluntary_context_switch (struct chx_thread *tp_next)
{
register uintptr_t result asm ("a0");
asm volatile (
/* Here, %0 (a0) points to pointer (struct chx_thread *) to be
* switched. We get the thread context pointer adding the
* offset.
*/
"# Voluntary context switch\n\t"
"sw sp,8(tp)\n\t"
"mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */
"sw zero,0(sp)\n\t"
SAVE_CALLEE_SAVE_REGISTERS
"# Check if going to IDLE function\n\t"
"bnez %0,0f\n\t"
/*
* NOTE: Here, for running chx_idle, we can use
* __main_stack_end__, because neither any threads, nor
* interrupt context use that. Using __main_stack_end__, we
* can minimize stack memory usage of each thread.
*/
"# Call the IDLE function, interrupt masked.\n\t"
"mv tp,zero\n\t"
"csrw mscratch,tp\n\t"
"la sp,__main_stack_end__\n\t"
"call chx_idle\n"
".L_V_CONTEXT_SWITCH_BEGIN:\n"
"0:\n\t"
"addi %0,%0,20\n\t"
"mv sp,%0\n\t"
RESTORE_CALLEE_SAVE_REGISTERS
/**/
"csrw mscratch,sp\n\t"
"lw a0,0(sp)\n\t"
"beqz a0,1f\n"
".L_RETURN_TO_PREEMPTED_THREAD:\n\t"
"csrw mepc,a0\n\t"
SETUP_MSTATUS_FROM_MACHINE_STATUS
RESTORE_OTHER_REGISTERS
"lw tp,16(sp)\n\t" /* Application is free to other use of TP */
"lw sp,8(sp)\n\t"
"mret\n"
"1:\n\t"
"lw a0,-4(sp)\n\t" /* Get the result value */
"mv tp,sp\n\t"
"lw sp,8(sp)\n\t"
"csrsi mstatus,8\n" /* Unmask interrupts. */
".L_V_CONTEXT_SWITCH_FINISH:"
: "=r" (result)
: "0" (tp_next)
: "ra", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
"a1", "a2", "a3", "a4", "a5", "a6", "a7",
"memory");
return result;
}
extern void cause_link_time_error_unexpected_size_of_struct_chx_thread (void);
static struct chx_thread *
chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
voidfunc thread_entry, void *arg)
{
struct chx_thread *tp;
void *stack;
register uint32_t gp;
asm ( "mv %0,gp" : "=r" (gp));
if (CHOPSTX_THREAD_SIZE != sizeof (struct chx_thread))
cause_link_time_error_unexpected_size_of_struct_chx_thread ();
if (stack_size < sizeof (struct chx_thread))
chx_fatal (CHOPSTX_ERR_THREAD_CREATE);
stack = (void *)(stack_addr + stack_size - sizeof (struct chx_thread));
tp = (struct chx_thread *)stack;
memset (&tp->tc, 0, sizeof (tp->tc));
tp->tc.reg[REG_A0] = (uint32_t)arg;
tp->tc.reg[REG_RA] = (uint32_t)chopstx_exit;
tp->tc.reg[REG_PC] = (uint32_t)thread_entry;
tp->tc.reg[REG_GP] = gp;
tp->tc.reg[REG_TP] = (uint32_t)&tp->tc;
tp->tc.reg[REG_SP] = (uint32_t)stack;
tp->tc.machine_status = MACHINE_STATUS_INIT;
return tp;
}
/*
* Note: Examine the assembler output carefully, because it has
* ((naked)) attribute
*/
static void __attribute__ ((naked,aligned(4)))
chx_handle_intr (void)
{
struct chx_thread *tp_next;
uint32_t irq_num;
/*
* stack setup to __main_stack_end__
* save registers.
*/
asm volatile (
"csrrw sp,mscratch,sp\n\t" /* SP to MSCRATCH, thread pointer into SP */
"# Check if it is IDLE\n\t"
"bnez sp,0f\n\t"
/**/
"csrrw sp,mscratch,sp\n\t" /* Recover MSCRATCH, the thread pointer */
"li a0,0x80\n\t"
"csrrc x0,mstatus,a0\n\t" /* Clear MPIE bit to mask interrupt */
"csrr a0,mcause\n\t"
"mret\n"
"0:\n\t"
"sw tp,16(sp)\n\t" /* Application is free to other use of TP */
SAVE_OTHER_REGISTERS
"csrr a0,mepc\n\t"
"sw a0,0(sp)\n\t"
/**/
"mv tp,sp\n\t" /* TP is now the thread pointer */
"csrrw sp,mscratch,sp\n\t" /* TP to MSCRATCH, SP_old into SP */
"sw sp,8(tp)\n\t"
"la sp,__main_stack_end__");
/*
* The stack at __main_stack_end__ is shared data between chx_idle
* and this handler. When it comes here, there is no active chx_idle,
* so, it is safe to use the stack.
*
* Note that when there are multiple cores, we need this stack for
* each core.
*/
asm ( "csrr %0,mcause\n\t"
"slli %0,%0,20\n\t"
"srli %0,%0,20" /* Take lower 12-bit of MCAUSE */
: "=r" (irq_num));
tp_next = NULL;
if (irq_num == SWINT_IRQ)
chx_sw_int (0);
else if (irq_num == TIMER_IRQ)
tp_next = chx_timer_expired ();
else if (irq_num == MEMERR_IRQ)
memory_error ();
else
tp_next = chx_recv_irq (irq_num);
if (!tp_next)
asm volatile (
"mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */
RESTORE_OTHER_REGISTERS
"lw tp,16(sp)\n\t" /* Application is free to other use of TP */
"lw sp,8(sp)\n\t"
"mret");
tp_next = chx_running_preempted (tp_next);
asm volatile (
"# Involuntary context switch\n\t"
"mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */
SAVE_CALLEE_SAVE_REGISTERS
/*
* MACHINE_STATUS = (MSTATUS & 0x00001f80)
*/
"csrr a1,mstatus\n\t"
"andi a1,a1,-9\n\t" /* Clear MIE (bit3) */
"slli a1,a1,19\n\t" /* Clear bit31 to bit13 */
"srli a1,a1,19\n\t" /* MPP..MPIE from MSTATUS */
"sw a1,128(sp)\n"
".L_IV_CONTEXT_SWITCH_BEGIN:\n\t"
"addi %0,%0,20\n\t"
"mv sp,%0\n\t"
RESTORE_CALLEE_SAVE_REGISTERS
/**/
"csrw mscratch,sp\n\t"
"lw a0,0(sp)\n\t"
"bnez a0,.L_RETURN_TO_PREEMPTED_THREAD\n\t"
/**/
"lw a0,-4(sp)\n\t" /* Get the result value */
"mv tp,sp\n\t"
"lw sp,8(sp)\n\t"
"la a1,.L_V_CONTEXT_SWITCH_FINISH\n\t"
"csrw mepc,a1\n\t"
"li a1,0x188\n\t" /* Set MPIE and MPP bits */
"slli a1,a1,4\n\t"
"csrrs x0,mstatus,a1\n\t"/* Prev: Machine mode, enable interrupt */
"mret" /* Return to Prev */
: /* no output */ : "r" (tp_next) : "memory");
}

View File

@@ -1,61 +0,0 @@
/*
* The thread context: specific to RISC-V 32.
*
* Calling convention matters when we implement context switch.
*
* We implement a voluntary context switch function which follows
* standard calling convension. The function doesn't need to save
* caller save registers (function arguments and temporaries) and
* fixed ones (global pointer and thread pointer) to a thread context
* struct. Only it needs to care about stack pointer and saved
* registers.
*
* For interrupt handling when it returns directly to original thread,
* we can avoid saving all registers; It can only save registers used
* in that handling. When it returns to another thread, that is, when
* it does involuntary context switch (also called "thread
* preemption"), all registers should be saved.
*
* In Chopstx, we don't use nested interrupt of machine. So, when a
* thread is interrupted, the interrupted context is always one of
* application threads or the idle thread. We have a dedicated stack
* for exception handling.
*
* Here is RISC-V calling convention.
*
* Register ABI name Description Saver other usage
* x0 zero 0, hard-wired --
* x1 ra return address caller
* x2 sp stack pointer callEE
* x3 gp global pointer --
* x4 tp thread pointer --
* x5-7 t0-2 temporary caller
* x8 s0/fp saved register callEE frame pointer
* x9 s1 saved register callEE
* x10-11 a0-1 argument caller return value
* x12-17 a2-7 argument caller
* x18-27 s2-11 saved register callEE
* x28-31 t3-6 temporary caller
*
* Simply, we have entries of all registers in the thread context. We
* don't use stack to save/restore part of its context. We don't put
* any other constraints such that an application should always keep
* gp and/or tp registers; It is free for an application to
* temporarily use those registers beyond a conventional usage (e.g.,
* for some specific routines like hash computation).
*
* We use the first entry, reg[0], to store PC, because x0 is always 0,
* no need to save/restore it.
*
* Core specific machine status information is saved into the last
* entry, MACHINE_STATUS. For Bumblebee core, it is composed by bits
* of mstatus and msubm.
*/
struct tcontext {
uint32_t reg[32];
uint32_t machine_status;
};
typedef struct tcontext tcontext_t;
#define CHOPSTX_THREAD_SIZE 160

335
chopstx.c
View File

@@ -1,7 +1,7 @@
/*
* chopstx.c - Threads and only threads.
*
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -73,7 +73,11 @@ chx_fatal (uint32_t err_code)
}
/* Include the definition of thread context structure. */
#include ARCH_HEADER
#ifdef GNU_LINUX_EMULATION
#include "chopstx-gnu-linux.h"
#else
#include "chopstx-cortex-m.h"
#endif
/* ALLOW_SLEEP for the idle thread. */
int chx_allow_sleep;
@@ -84,6 +88,9 @@ struct chx_queue {
struct chx_spinlock lock;
};
/* Forward declaration. */
struct chx_pq;
/* READY: priority queue. */
static struct chx_queue q_ready;
@@ -97,8 +104,9 @@ static struct chx_queue q_join;
static struct chx_queue q_intr;
/* Forward declaration(s). */
static void chx_request_preemption (uint16_t prio);
static int chx_wakeup (struct chx_pq *p);
static struct chx_thread *chx_timer_insert (struct chx_thread *tp, uint32_t usec);
static struct chx_thread * chx_timer_insert (struct chx_thread *tp, uint32_t usec);
static uint32_t chx_timer_dequeue (struct chx_thread *tp);
@@ -121,7 +129,7 @@ static void chx_spin_unlock (struct chx_spinlock *lk)
/**************/
struct chx_pq {
struct chx_pq *next, *prev;
struct chx_qh q;
uint32_t : 4;
uint32_t : 5;
uint32_t : 6;
@@ -133,7 +141,7 @@ struct chx_pq {
};
struct chx_px { /* inherits PQ */
struct chx_pq *next, *prev;
struct chx_qh q;
uint32_t : 4;
uint32_t : 5;
uint32_t : 6;
@@ -149,7 +157,7 @@ struct chx_px { /* inherits PQ */
};
struct chx_thread { /* inherits PQ */
struct chx_pq *next, *prev;
struct chx_qh q;
uint32_t state : 4;
uint32_t flag_detached : 1;
uint32_t flag_got_cancel : 1;
@@ -175,63 +183,69 @@ struct chx_thread { /* inherits PQ */
static int
ll_empty (struct chx_qh *q)
{
return q == (struct chx_qh *)q->next;
return q == q->next;
}
static struct chx_pq *
ll_dequeue (struct chx_pq *pq)
{
pq->next->prev = pq->prev;
pq->prev->next = pq->next;
pq->prev = pq->next = pq;
pq->q.next->prev = pq->q.prev;
pq->q.prev->next = pq->q.next;
pq->q.prev = pq->q.next = &pq->q;
return pq;
}
static void
ll_insert (struct chx_pq *pq0, struct chx_qh *q)
ll_insert (struct chx_qh *q0, struct chx_qh *q)
{
struct chx_pq *pq = (struct chx_pq *)q;
pq0->next = (struct chx_pq *)pq;
pq0->prev = pq->prev;
pq->prev->next = (struct chx_pq *)pq0;
pq->prev = pq0;
q0->next = q;
q0->prev = q->prev;
q->prev->next = q0;
q->prev = q0;
}
static struct chx_pq *
ll_pop (struct chx_qh *q)
{
if (q == (struct chx_qh *)q->next)
if (q == q->next)
return NULL;
return ll_dequeue (q->next);
return ll_dequeue ((struct chx_pq *)q->next);
}
static void
ll_prio_push (struct chx_pq *pq0, struct chx_qh *q0)
{
struct chx_pq *p;
struct chx_qh *q;
for (p = q0->next; p != (struct chx_pq *)q0; p = p->next)
if (p->prio <= pq0->prio)
break;
for (q = q0->next; q != q0; q = q->next)
{
struct chx_pq *p = (struct chx_pq *)q;
if (p->prio <= pq0->prio)
break;
}
pq0->parent = q0;
ll_insert (pq0, (struct chx_qh *)p);
ll_insert (&pq0->q, q);
}
static void
ll_prio_enqueue (struct chx_pq *pq0, struct chx_qh *q0)
{
struct chx_pq *p;
struct chx_qh *q;
for (p = q0->next; p != (struct chx_pq *)q0; p = p->next)
if (p->prio < pq0->prio)
break;
for (q = q0->next; q != q0; q = q->next)
{
struct chx_pq *p = (struct chx_pq *)q;
if (p->prio < pq0->prio)
break;
}
pq0->parent = q0;
ll_insert (pq0, (struct chx_qh *)p);
ll_insert (&pq0->q, q);
}
@@ -292,40 +306,46 @@ chx_ready_enqueue (struct chx_thread *tp)
chx_spin_unlock (&q_ready.lock);
}
static struct chx_thread *chx_timer_expired (void);
static struct chx_thread *chx_recv_irq (uint32_t irq_num);
static struct chx_thread *chx_running_preempted (struct chx_thread *tp_next);
/*
* Here comes architecture specific code.
*/
#include ARCH_IMPL
#ifdef GNU_LINUX_EMULATION
#include "chopstx-gnu-linux.c"
#else
#include "chopstx-cortex-m.c"
#endif
static void
chx_set_timer (struct chx_thread *tp, uint32_t ticks)
chx_set_timer (struct chx_qh *q, uint32_t ticks)
{
if (tp == (struct chx_thread *)&q_timer.q)
if (q == &q_timer.q)
chx_systick_reload (ticks);
else
tp->v = ticks;
{
struct chx_thread *tp = (struct chx_thread *)q;
tp->v = ticks;
}
}
static struct chx_thread *
chx_timer_insert (struct chx_thread *tp, uint32_t usec)
{
struct chx_pq *p;
struct chx_qh *q;
uint32_t ticks = usec_to_ticks (usec);
uint32_t next_ticks = chx_systick_get ();
for (p = q_timer.q.next; p != (struct chx_pq *)&q_timer.q; p = p->next)
for (q = q_timer.q.next; q != &q_timer.q; q = q->next)
{
struct chx_pq *p = (struct chx_pq *)q;
if (ticks < next_ticks)
{
tp->parent = &q_timer.q;
ll_insert ((struct chx_pq *)tp, (struct chx_qh *)p);
chx_set_timer ((struct chx_thread *)tp->prev, ticks);
chx_set_timer (tp, (next_ticks - ticks));
ll_insert (&tp->q, q);
chx_set_timer (tp->q.prev, ticks);
chx_set_timer (&tp->q, (next_ticks - ticks));
break;
}
else
@@ -335,12 +355,12 @@ chx_timer_insert (struct chx_thread *tp, uint32_t usec)
}
}
if (p == (struct chx_pq *)&q_timer.q)
if (q == &q_timer.q)
{
tp->parent = &q_timer.q;
ll_insert ((struct chx_pq *)tp, (struct chx_qh *)p);
chx_set_timer ((struct chx_thread *)tp->prev, ticks);
chx_set_timer (tp, 1); /* Non-zero for the last entry. */
ll_insert (&tp->q, q);
chx_set_timer (tp->q.prev, ticks);
chx_set_timer (&tp->q, 1); /* Non-zero for the last entry. */
}
return tp;
@@ -350,25 +370,30 @@ chx_timer_insert (struct chx_thread *tp, uint32_t usec)
static uint32_t
chx_timer_dequeue (struct chx_thread *tp)
{
struct chx_thread *tp_prev;
struct chx_qh *q_prev;
uint32_t ticks_remained;
chx_spin_lock (&q_timer.lock);
ticks_remained = chx_systick_get ();
tp_prev = (struct chx_thread *)tp->prev;
if (tp_prev == (struct chx_thread *)&q_timer.q)
q_prev = tp->q.prev;
if (q_prev == &q_timer.q)
{
if (tp->next == (struct chx_pq *)&q_timer.q)
if (tp->q.next == &q_timer.q)
chx_systick_reload (0); /* Cancel timer. */
else
chx_systick_reload (ticks_remained + tp->v); /* Update timer. */
}
else
{
struct chx_pq *p;
struct chx_thread *tp_prev = (struct chx_thread *)q_prev;
struct chx_qh *q;
for (p = q_timer.q.next; p != (struct chx_pq *)tp; p = p->next)
ticks_remained += p->v;
for (q = q_timer.q.next; q != &q_timer.q; q = q->next)
{
struct chx_pq *p = (struct chx_pq *)q;
ticks_remained += p->v;
}
tp_prev->v += tp->v;
}
@@ -379,7 +404,7 @@ chx_timer_dequeue (struct chx_thread *tp)
}
static struct chx_thread *
void
chx_timer_expired (void)
{
struct chx_thread *tp;
@@ -387,9 +412,7 @@ chx_timer_expired (void)
uint16_t prio = 0; /* Use uint16_t here. */
chx_spin_lock (&q_timer.lock);
if (!(tp = (struct chx_thread *)ll_pop (&q_timer.q)))
chx_systick_reload (0);
else
if ((tp = (struct chx_thread *)ll_pop (&q_timer.q)))
{
uint32_t next_tick = tp->v;
@@ -401,19 +424,19 @@ chx_timer_expired (void)
if ((uint16_t)tp->prio > prio)
prio = (uint16_t)tp->prio;
if (ll_empty (&q_timer.q))
chx_systick_reload (0);
else
if (!ll_empty (&q_timer.q))
{
struct chx_thread *tp_next;
struct chx_qh *q, *q_next;
for (tp = (struct chx_thread *)q_timer.q.next;
tp != (struct chx_thread *)&q_timer.q && next_tick == 0;
tp = tp_next)
for (q = q_timer.q.next;
q != &q_timer.q && next_tick == 0;
q = q_next)
{
tp = (struct chx_thread *)q;
next_tick = tp->v;
tp->v = (uintptr_t)0;
tp_next = (struct chx_thread *)tp->next;
q_next = tp->q.next;
ll_dequeue ((struct chx_pq *)tp);
chx_ready_enqueue (tp);
if (tp == running)
@@ -423,115 +446,13 @@ chx_timer_expired (void)
prio = (uint16_t)tp->prio;
}
if (ll_empty (&q_timer.q))
chx_systick_reload (0);
else
chx_set_timer ((struct chx_thread *)&q_timer.q, next_tick);
if (!ll_empty (&q_timer.q))
chx_set_timer (&q_timer.q, next_tick);
}
}
chx_spin_unlock (&q_timer.lock);
if (running == NULL || (uint16_t)running->prio < prio)
{
tp = chx_ready_pop ();
if (tp != running)
return tp;
else
/* When tp->flag_sched_rr == 1, it's possible. No context switch. */
return NULL;
}
else
return NULL;
}
static struct chx_thread *
chx_recv_irq (uint32_t irq_num)
{
struct chx_pq *p;
struct chx_thread *r = chx_running ();
chx_disable_intr (irq_num);
chx_spin_lock (&q_intr.lock);
for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next)
if (p->v == irq_num)
/* should be one at most. */
break;
chx_spin_unlock (&q_intr.lock);
if (p)
{
struct chx_px *px = (struct chx_px *)p;
ll_dequeue (p);
chx_wakeup (p);
if (r == NULL || (uint16_t)r->prio < px->master->prio)
return chx_ready_pop ();
}
return NULL;
}
static struct chx_thread * __attribute__ ((noinline))
chx_running_preempted (struct chx_thread *tp_next)
{
struct chx_thread *r = chx_running ();
if (r == NULL)
return tp_next;
if (r->flag_sched_rr)
{
if (r->state == THREAD_RUNNING)
{
chx_timer_dequeue (r);
chx_ready_enqueue (r);
}
/*
* It may be THREAD_READY after chx_timer_expired.
* Then, do nothing. It's in the ready queue.
*/
}
else
chx_ready_push (r);
return tp_next;
}
/*
* chx_sched: switch to another thread.
*
* There are two cases:
* YIELD=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* YIELD=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*
* Returns:
* >= 1 on wakeup by others, value means ticks remained for sleep.
* 0 on normal wakeup (timer expiration, lock acquirement).
* -1 on cancellation.
*/
static uintptr_t __attribute__ ((noinline))
chx_sched (uint32_t yield)
{
struct chx_thread *tp;
if (yield)
{
struct chx_thread *r = chx_running ();
if (r->flag_sched_rr)
chx_timer_dequeue (r);
chx_ready_enqueue (r);
}
tp = chx_ready_pop ();
return voluntary_context_switch (tp);
chx_request_preemption (prio);
}
@@ -561,15 +482,15 @@ chx_init (struct chx_thread *tp)
chx_init_arch (tp);
chx_spin_init (&chx_enable_sleep_lock);
q_ready.q.next = q_ready.q.prev = (struct chx_pq *)&q_ready.q;
q_ready.q.next = q_ready.q.prev = &q_ready.q;
chx_spin_init (&q_ready.lock);
q_timer.q.next = q_timer.q.prev = (struct chx_pq *)&q_timer.q;
q_timer.q.next = q_timer.q.prev = &q_timer.q;
chx_spin_init (&q_timer.lock);
q_join.q.next = q_join.q.prev = (struct chx_pq *)&q_join.q;
q_join.q.next = q_join.q.prev = &q_join.q;
chx_spin_init (&q_join.lock);
q_intr.q.next = q_intr.q.prev = (struct chx_pq *)&q_intr.q;
q_intr.q.next = q_intr.q.prev = &q_intr.q;
chx_spin_init (&q_intr.lock);
tp->next = tp->prev = (struct chx_pq *)tp;
tp->q.next = tp->q.prev = &tp->q;
tp->mutex_list = NULL;
tp->clp = NULL;
tp->state = THREAD_RUNNING;
@@ -583,6 +504,9 @@ chx_init (struct chx_thread *tp)
tp->parent = NULL;
tp->v = 0;
if (CHX_PRIO_MAIN_INIT >= CHOPSTX_PRIO_INHIBIT_PREEMPTION)
chx_cpu_sched_lock ();
tp->prio = CHX_PRIO_MAIN_INIT;
chopstx_main = (chopstx_t)tp;
@@ -640,20 +564,24 @@ chx_wakeup (struct chx_pq *pq)
static void __attribute__((noreturn))
chx_exit (void *retval)
{
struct chx_pq *p;
struct chx_qh *q;
struct chx_thread *running = chx_running ();
chx_cpu_sched_lock ();
if (running->flag_join_req)
{ /* wake up a thread which requests to join */
chx_spin_lock (&q_join.lock);
for (p = q_join.q.next; p != (struct chx_pq *)&q_join.q; p = p->next)
if (p->v == (uintptr_t)running)
{ /* should be one at most. */
ll_dequeue (p);
chx_wakeup (p);
break;
}
for (q = q_join.q.next; q != &q_join.q; q = q->next)
{
struct chx_pq *p = (struct chx_pq *)q;
if (p->v == (uintptr_t)running)
{ /* should be one at most. */
ll_dequeue (p);
chx_wakeup (p);
break;
}
}
chx_spin_unlock (&q_join.lock);
}
@@ -697,9 +625,14 @@ chx_mutex_unlock (chopstx_mutex_t *mutex)
/* Examine mutexes we hold, and determine new priority for running. */
for (m = running->mutex_list; m; m = m->list)
if (!ll_empty (&m->q)
&& ((struct chx_thread *)(m->q.next))->prio > newprio)
newprio = ((struct chx_thread *)m->q.next)->prio;
if (!ll_empty (&m->q))
{
struct chx_thread *tp_m = (struct chx_thread *)m->q.next;
uint16_t prio_m = tp_m->prio;
if (prio_m > newprio)
newprio = prio_m;
}
/* Then, assign it. */
running->prio = newprio;
@@ -730,7 +663,7 @@ chopstx_create (uint32_t flags_and_prio,
tp = chopstx_create_arch (stack_addr, stack_size, thread_entry,
arg);
tp->next = tp->prev = (struct chx_pq *)tp;
tp->q.next = tp->q.prev = &tp->q;
tp->mutex_list = NULL;
tp->clp = NULL;
tp->state = THREAD_EXITED;
@@ -842,7 +775,7 @@ void
chopstx_mutex_init (chopstx_mutex_t *mutex)
{
chx_spin_init (&mutex->lock);
mutex->q.next = mutex->q.prev = (struct chx_pq *)&mutex->q;
mutex->q.next = mutex->q.prev = &mutex->q;
mutex->list = NULL;
mutex->owner = NULL;
}
@@ -978,7 +911,7 @@ void
chopstx_cond_init (chopstx_cond_t *cond)
{
chx_spin_init (&cond->lock);
cond->q.next = cond->q.prev = (struct chx_pq *)&cond->q;
cond->q.next = cond->q.prev = &cond->q;
}
@@ -1470,7 +1403,7 @@ chx_proxy_init (struct chx_px *px, uint32_t *cp)
{
struct chx_thread *running = chx_running ();
px->next = px->prev = (struct chx_pq *)px;
px->q.next = px->q.prev = &px->q;
px->flag_is_proxy = 1;
px->prio = running->prio;
px->parent = NULL;
@@ -1660,7 +1593,7 @@ chopstx_setpriority (chopstx_prio_t prio_new)
if (tp->prio < prio_cur)
chx_sched (CHX_YIELD);
else
else if (tp->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION)
chx_cpu_sched_unlock ();
return prio_orig;
@@ -1700,23 +1633,3 @@ chopstx_conf_idle (int enable_sleep)
return r;
}
/**
* chopstx_critical - Construct a critical section
* @func: function to be called
* @arg: argument to function
*
* Run the function @func with @arg under CPU schedule lock.
* Return void * pointer.
*/
void *
chopstx_critical (void *func (void *), void *arg)
{
void *p;
chx_cpu_sched_lock ();
p = func (arg);
chx_cpu_sched_unlock ();
return p;
}

View File

@@ -1,8 +1,7 @@
/*
* chopstx.h - Threads and only threads.
*
* Copyright (C) 2013, 2016, 2017, 2018, 2020
* Flying Stone Technology
* Copyright (C) 2013, 2016, 2017, 2018 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
@@ -28,7 +27,7 @@
*/
struct chx_qh {
struct chx_pq *next, *prev;
struct chx_qh *next, *prev;
};
typedef uintptr_t chopstx_t;
@@ -46,6 +45,8 @@ chopstx_create (uint32_t flags_and_prio,
#define CHOPSTX_DETACHED 0x10000
#define CHOPSTX_SCHED_RR 0x20000
#define CHOPSTX_PRIO_INHIBIT_PREEMPTION 248
void chopstx_usec_wait (uint32_t usec);
struct chx_spinlock {
@@ -162,5 +163,3 @@ int chopstx_poll (uint32_t *usec_p, int n,
struct chx_poll_head *const pd_array[]);
int chopstx_conf_idle (int enable_sleep);
void *chopstx_critical (void *func (void *), void *arg);

232
contrib/ackbtn-gnu-linux.c Normal file
View File

@@ -0,0 +1,232 @@
/*
* ackbtn-gnu-linux.c - Acknowledge button support for GNU/Linux
*
* Copyright (C) 2021 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <chopstx.h>
#include <stdio.h>
#include <signal.h>
#include <sys/eventfd.h>
#include <poll.h>
#include <errno.h>
#include <stdlib.h>
static pthread_t tid_main;
static pthread_t tid_ui;
static pthread_mutex_t mutex;
static pthread_cond_t cond;
static int enabled;
static int event_fd;
static void
ackbtn_intr (int signum, siginfo_t *siginfo, void *arg)
{
extern void chx_sigmask (ucontext_t *uc);
extern void chx_handle_intr (int signum);
ucontext_t *uc = arg;
(void)signum;
(void)siginfo;
chx_handle_intr (SIGUSR2);
chx_sigmask (uc);
}
#define ACKBTN_ACKED (1 << 0)
#define ACKBTN_TIMEOUT (1 << 2)
static void *
user_interaction (void *arg)
{
struct pollfd pollfds[2];
(void)arg;
pollfds[0].fd = 0; /* standard input */
pollfds[0].events = POLLIN;
pollfds[1].fd = event_fd;
pollfds[1].events = POLLIN;
fputs ("User interaction thread for AckBtn started.\n", stdout);
while (1)
{
unsigned int acked_or_timeout = 0;
char buf[256];
pthread_mutex_lock (&mutex);
while (!enabled)
pthread_cond_wait (&cond, &mutex);
pthread_mutex_unlock (&mutex);
/* Consume all input if any. */
while (1)
{
int r;
pollfds[0].revents = 0;
r = poll (pollfds, 1, 0);
if (r < 0)
{
if (errno == EINTR)
continue;
perror ("poll");
exit (1);
}
if (r == 0)
break;
if ((pollfds[0].revents & POLLIN))
read (0, buf, sizeof buf);
}
fputs ("Type RET to acknowledge (or wait for timeout) > ", stdout);
fflush (stdout);
while (!acked_or_timeout)
{
pollfds[0].revents = 0;
pollfds[1].revents = 0;
if (poll (pollfds, 2, -1) < 0)
{
if (errno == EINTR)
continue;
perror ("poll");
exit (1);
}
if ((pollfds[0].revents & POLLIN))
{
read (0, buf, sizeof buf);
acked_or_timeout |= ACKBTN_ACKED;
}
if ((pollfds[1].revents & POLLIN))
acked_or_timeout |= ACKBTN_TIMEOUT;
}
pthread_mutex_lock (&mutex);
if ((acked_or_timeout & ACKBTN_ACKED))
{
if ((acked_or_timeout & ACKBTN_TIMEOUT))
/* No output of newline, as it follows timeout message. */
fputs ("Input ignored", stdout);
else
{
pthread_kill (tid_main, SIGUSR2);
fputs ("Acknowledged\n", stdout);
}
}
if ((acked_or_timeout & ACKBTN_TIMEOUT))
fputs ("\nTimeout\n", stdout);
while (enabled)
pthread_cond_wait (&cond, &mutex);
read (event_fd, buf, sizeof (uint64_t));
pthread_mutex_unlock (&mutex);
}
}
void
ackbtn_init (chopstx_intr_t *intr)
{
int r;
sigset_t sigset;
struct sigaction act;
event_fd = eventfd (0, EFD_CLOEXEC);
if (event_fd < 0)
{
perror ("eventfd");
exit (1);
}
pthread_mutex_init (&mutex, NULL);
pthread_cond_init (&cond, NULL);
enabled = 0;
sigemptyset (&sigset);
sigaddset (&sigset, SIGUSR2);
sigaddset (&sigset, SIGALRM);
tid_main = pthread_self ();
/* Launch the thread for user interaction. */
pthread_sigmask (SIG_BLOCK, &sigset, NULL);
r = pthread_create (&tid_ui, NULL, user_interaction, NULL);
if (r)
{
fprintf (stderr, "ackbtn_init: %s\n", strerror (r));
exit (1);
}
act.sa_sigaction = ackbtn_intr;
sigfillset (&act.sa_mask);
act.sa_flags = SA_SIGINFO|SA_RESTART;
sigaction (SIGUSR2, &act, NULL);
pthread_sigmask (SIG_UNBLOCK, &sigset, NULL);
chopstx_claim_irq (intr, SIGUSR2);
}
void
ackbtn_enable (void)
{
pthread_mutex_lock (&mutex);
enabled = 1;
pthread_cond_signal (&cond);
pthread_mutex_unlock (&mutex);
}
void
ackbtn_disable (void)
{
const uint64_t l = 1;
pthread_mutex_lock (&mutex);
enabled = 0;
write (event_fd, &l, sizeof (l));
pthread_cond_signal (&cond);
pthread_mutex_unlock (&mutex);
}

View File

@@ -66,6 +66,15 @@ ackbtn_init (chopstx_intr_t *intr)
pin_config |= PINCFG_EDGE_RISING;
break;
case BOARD_ID_ST_DONGLE:
/* PA5 is connected to a switch */
afio_exticr_index = 0;
afio_exticr_extiX_pY = AFIO_EXTICR2_EXTI5_PA;
irq_num = EXTI9_5_IRQ;
pin_config = 0x0020; /* EXTI_PR_PR5 == EXTI_IMR_MR5 == EXTI_RTSR_TR5 */
pin_config |= PINCFG_EDGE_RISING;
break;
case BOARD_ID_FST_01SZ:
default:
/* PA3 is connected to a hall sensor DRV5032FA */

View File

@@ -3,7 +3,7 @@
* This ADC driver just fills pseudo random values.
* It's completely useless other than for NeuG.
*
* Copyright (C) 2017 Free Software Initiative of Japan
* Copyright (C) 2017, 2021 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
@@ -33,7 +33,7 @@
#include <chopstx.h>
#include "adc.h"
#define ADC_RANDOM_SEED 0x01034649 /* "Hello, father!" in Japanese */
#include <sys/random.h>
/*
* Do calibration for ADC.
@@ -41,7 +41,6 @@
int
adc_init (void)
{
srandom (ADC_RANDOM_SEED);
return 0;
}
@@ -55,8 +54,7 @@ uint32_t adc_buf[64];
void
adc_start_conversion (int offset, int count)
{
while (count--)
adc_buf[offset++] = random ();
getrandom (adc_buf+offset, count, 0);
}

View File

@@ -1,207 +0,0 @@
/*
* spi-st.c - SPI driver for STM32F103/GD32F103/GD32VF103
*
* Copyright (C) 2019 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <mcu/stm32.h>
#ifndef SPI_USE_INTERRUPT
#define SPI_BUSY_WAIT 1
#endif
/* Hardware registers */
struct SPI {
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t SR;
volatile uint32_t DR;
volatile uint32_t CRCPR;
volatile uint32_t RXCRCR;
volatile uint32_t TXCRCR;
};
/* It is named SPI0 in GD32VF103, while it's "SPI1" in RM0008 of STM32F103 */
#define SPI0_BASE (APB2PERIPH_BASE + 0x3000)
#define SPI0 ((struct SPI *)SPI0_BASE)
#define INTR_REQ_SPI0 54 /* For GD32VF103 */
static struct SPI *SPIx = SPI0;
#ifndef SPI_BUSY_WAIT
static struct chx_intr spi_intr;
#endif
static int tx_ready;
static int rx_done;
/*
* For now, it only support configuration of a specific use case:
*
* SPI0 of GD32VF103
* Master mode
* Hardware NSS pin not used
* Two unidirectional lines, full duplex
* CRC enabled
* 8-bit data frame
* MSBit first
* Clock polarity: High==idle
*
* We need to consider what kind of configuration can be specified
* by the API. Perhaps, it should return an error code when it is
* not supported by the hardware.
*/
int
spi_init (void)
{
uint32_t cr1_reg = 0;
/* Enable SPI module */
RCC->APB2ENR |= (1 << 12);
RCC->APB2RSTR = (1 << 12);
RCC->APB2RSTR = 0;
/* Master mode */
cr1_reg |= (1 << 2); /* In RM0008, it's called MSTR */
/* No use of hardware NSS pin */
cr1_reg |= (1 << 9); /* In RM0008, it's called SSM */
cr1_reg |= (1 << 8); /* In RM0008, it's called SSI */
/* Two unidirectional lines, full duplex: Bit15=0, Bit14=0, Bit10=0 */
/* Bit11: data-frame-format: 8-bit 0 (16-bit (1<<11)) */
/* But7: bit-order: MSB first 0, LSB first (1<<7) */
/* Enable CRC */
cr1_reg |= (1 << 13);
cr1_reg |= (0x02 << 3); /* prescaler: fPCLK/8 */
cr1_reg |= (1 << 1); /* clock polarity 1==idle */
cr1_reg |= (1 << 0); /* second clock data capture edge */
cr1_reg |= (1 << 6); /* SPI enable */
SPIx->CR1 = cr1_reg;
/* Don't touch CRCPR: Just use reset default value of 7 */
/* Don't touch I2SCFGR, I2SPR */
#ifndef SPI_BUSY_WAIT
chopstx_claim_irq (&spi_intr, INTR_REQ_SPI0);
/* Enable notification for Tx empty, Rx done, or Error */
SPIx->CR2 = (1 << 7) | (1 << 6) | (1 << 5);
#endif
tx_ready = 0;
rx_done = 0;
return 0;
}
static void
clear_crc_err (void)
{
SPIx->SR &= ~(1 << 4);
}
static void
put_data (uint16_t data)
{
tx_ready = 0;
rx_done = 0;
SPIx->DR = (uint32_t)data;
}
static uint16_t
get_data (void)
{
return ((uint16_t)SPIx->DR);
}
#define CHECK_TX 0
#define CHECK_RX 1
static void
check_transmit (int for_what)
{
for (;;)
{
uint16_t status;
if (for_what == CHECK_TX && tx_ready)
return;
if (for_what == CHECK_RX && rx_done)
return;
#ifndef SPI_BUSY_WAIT
/* Wait an event */
chopstx_intr_wait (&spi_intr);
#endif
status = SPIx->SR;
if ((status & (1 << 0)))
rx_done = 1;
if ((status & (1 << 1)))
tx_ready = 1;
if ((status & (1 << 4)))
{
/*FIXME: stat crc_err_count++ */
clear_crc_err ();
}
#if 0
/*FIXME: stat err_count++ */
if ((status & 0x00fc))
{
}
#endif
#ifndef SPI_BUSY_WAIT
chopstx_intr_done (&spi_intr);
#endif
}
}
void
spi_send (uint16_t data)
{
check_transmit (CHECK_TX);
put_data (data);
}
uint16_t
spi_recv (void)
{
check_transmit (CHECK_RX);
return get_data ();
}

View File

@@ -1,3 +0,0 @@
int spi_init (void);
void spi_send (uint16_t data);
uint16_t spi_recv (void);

View File

@@ -1,386 +0,0 @@
static void
usart_config_recv_enable (struct USART *USARTx, int on)
{
if (on)
USARTx->CR1 |= USART_CR1_RE;
else
USARTx->CR1 &= ~USART_CR1_RE;
}
int
usart_config (uint8_t dev_no, uint32_t config_bits)
{
struct USART *USARTx = get_usart_dev (dev_no);
uint8_t baud_spec = (config_bits & MASK_BAUD);
int i;
uint32_t cr1_config = (USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE);
/* TXEIE/TCIE will be enabled when
putting char */
/* No CTSIE, PEIE, IDLEIE, LBDIE */
if (USARTx == NULL)
return -1;
/* Disable USART before configure. */
USARTx->CR1 &= ~USART_CR1_UE;
if (((config_bits & MASK_CS) == CS7 && (config_bits & PARENB))
|| ((config_bits & MASK_CS) == CS8 && (config_bits & PARENB) == 0))
cr1_config &= ~USART_CR1_M;
else if ((config_bits & MASK_CS) == CS8)
cr1_config |= USART_CR1_M;
else
return -1;
if ((config_bits & PARENB) == 0)
cr1_config &= ~(USART_CR1_PCE | USART_CR1_PEIE);
else
cr1_config |= (USART_CR1_PCE | USART_CR1_PEIE);
if ((config_bits & PARODD) == 0)
cr1_config &= ~USART_CR1_PS;
else
cr1_config |= USART_CR1_PS;
if ((config_bits & MASK_STOP) == STOP0B5)
USARTx->CR2 = (0x1 << 12);
else if ((config_bits & MASK_STOP) == STOP1B)
USARTx->CR2 = (0x0 << 12);
else if ((config_bits & MASK_STOP) == STOP1B5)
USARTx->CR2 = (0x3 << 12);
else /* if ((config_bits & MASK_STOP) == STOP2B) */
USARTx->CR2 = (0x2 << 12);
for (i = 0; i < NUM_BAUD; i++)
if (brr_table[i].baud_spec == baud_spec)
break;
if (i >= NUM_BAUD)
return -1;
USARTx->BRR = brr_table[i].brr_value;
if ((config_bits & MASK_FLOW))
USARTx->CR3 = USART_CR3_CTSE | USART_CR3_RTSE;
else
USARTx->CR3 = 0;
if (!(config_bits & MASK_MODE))
cr1_config |= USART_CR1_RE;
USARTx->CR1 = cr1_config;
/* SCEN (smartcard enable) should be set _after_ CR1. */
if ((config_bits & MASK_MODE))
{
if ((config_bits & MASK_MODE) == MODE_SMARTCARD)
{
USARTx->GTPR = (1 << 8) | 5;
USARTx->CR3 |= (USART_CR3_SCEN | USART_CR3_NACK);
}
else if ((config_bits & MASK_MODE) == MODE_IRDA)
USARTx->CR3 |= (1 << 1);
else if ((config_bits & MASK_MODE) == MODE_IRDA_LP)
USARTx->CR3 |= (1 << 2) | (1 << 1);
}
return 0;
}
void
usart_init0 (int (*cb) (uint8_t dev_no, uint16_t notify_bits))
{
int i;
ss_notify_callback = cb;
for (i = 0; i < NUM_USART; i++)
{
if (usart_array[i].stat)
usart_array[i].stat->dev_no = i + USART_DEVNO_START;
chopstx_claim_irq (usart_array[i].intr, usart_array[i].irq_num);
}
usart_rcc_setup ();
}
#define UART_STATE_BITMAP_RX_CARRIER (1 << 0)
#define UART_STATE_BITMAP_TX_CARRIER (1 << 1)
#define UART_STATE_BITMAP_BREAK (1 << 2)
#define UART_STATE_BITMAP_RINGSIGNAL (1 << 3)
#define UART_STATE_BITMAP_FRAMING (1 << 4)
#define UART_STATE_BITMAP_PARITY (1 << 5)
#define UART_STATE_BITMAP_OVERRUN (1 << 6)
static int
handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
{
int tx_ready = 0;
uint32_t r = USARTx->SR;
int notify_bits = 0;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
if (smartcard_mode)
{
if ((r & USART_SR_TC))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TCIE;
}
}
else
{
if ((r & USART_SR_TXE))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TXEIE;
}
}
if ((r & USART_SR_RXNE))
{
uint32_t data = USARTx->DR;
/* DR register should be accessed even if data is not used.
* Its read-access has side effect of clearing error flags.
*/
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_SR_NE))
stat->err_rx_noise++;
else if ((r & USART_SR_FE))
{
/* NOTE: Noway to distinguish framing error and break */
stat->rx_break++;
notify_bits |= UART_STATE_BITMAP_BREAK;
}
else if ((r & USART_SR_PE))
{
stat->err_rx_parity++;
notify_bits |= UART_STATE_BITMAP_PARITY;
}
else
{
if ((r & USART_SR_ORE))
{
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
/* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */
if (rb_ll_put (rb2a, (data & 0xff)) < 0)
stat->err_rx_overflow++;
else
stat->rx++;
}
}
else if ((r & USART_SR_ORE))
{ /* Clear ORE */
uint32_t data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
if (notify_bits)
{
if (ss_notify_callback
&& (*ss_notify_callback) (stat->dev_no, notify_bits))
stat->err_notify_overflow++;
}
return tx_ready;
}
static int
handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat)
{
int tx_ready = 1;
int c = rb_ll_get (rb2h);
if (c >= 0)
{
uint32_t r;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
USARTx->DR = (c & 0xff);
stat->tx++;
r = USARTx->SR;
if (smartcard_mode)
{
if ((r & USART_SR_TC) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TCIE;
}
}
else
{
if ((r & USART_SR_TXE) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TXEIE;
}
}
}
return tx_ready;
}
int
usart_send_break (uint8_t dev_no)
{
struct USART *USARTx = get_usart_dev (dev_no);
if (USARTx == NULL)
return -1;
if ((USARTx->CR1 & 0x01))
return 1; /* Busy sending break, which was requested before. */
USARTx->CR1 |= 0x01;
return 0;
}
int
usart_block_sendrecv (uint8_t dev_no, const char *s_buf, uint16_t s_buflen,
char *r_buf, uint16_t r_buflen,
uint32_t *timeout_block_p, uint32_t timeout_char)
{
uint32_t timeout;
uint8_t *p;
int len;
uint32_t r;
uint32_t data;
struct USART *USARTx = get_usart_dev (dev_no);
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
struct chx_intr *usartx_intr = get_usart_intr (dev_no);
struct chx_poll_head *ph[1];
if (usartx_intr == NULL)
return -1;
ph[0] = (struct chx_poll_head *)usartx_intr;
p = (uint8_t *)s_buf;
if (p)
{
if (smartcard_mode)
usart_config_recv_enable (USARTx, 0);
USARTx->CR1 |= USART_CR1_TXEIE;
/* Sending part */
while (1)
{
chopstx_poll (NULL, 1, ph);
r = USARTx->SR;
/* Here, ignore recv error(s). */
if ((r & USART_SR_RXNE) || (r & USART_SR_ORE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
if ((r & USART_SR_TXE))
{
if (s_buflen == 0)
break;
else
{
/* Keep TXEIE bit */
USARTx->DR = *p++;
s_buflen--;
}
}
chopstx_intr_done (usartx_intr);
}
USARTx->CR1 &= ~USART_CR1_TXEIE;
if (smartcard_mode)
{
if (timeout_block_p && (*timeout_block_p))
do
r = USARTx->SR;
while (((r & USART_SR_TC) == 0));
usart_config_recv_enable (USARTx, 1);
if (timeout_block_p && *timeout_block_p == 0)
{
/* Ignoring the echo back. */
do
r = USARTx->SR;
while (((r & USART_SR_TC) == 0));
if ((r & USART_SR_RXNE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
*timeout_block_p = timeout_char;
}
}
chopstx_intr_done (usartx_intr);
}
if (r_buf == NULL)
return 0;
if (!p)
if (smartcard_mode)
usart_config_recv_enable (USARTx, 1);
/* Receiving part */
r = chopstx_poll (timeout_block_p, 1, ph);
if (r == 0)
return 0;
p = (uint8_t *)r_buf;
len = 0;
while (1)
{
r = USARTx->SR;
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_SR_RXNE))
{
if ((r & USART_SR_NE) || (r & USART_SR_FE) || (r & USART_SR_PE))
/* ignore error, for now. XXX: ss_notify */
;
else
{
*p++ = (data & 0xff);
len++;
r_buflen--;
if (r_buflen == 0)
{
chopstx_intr_done (usartx_intr);
break;
}
}
}
else if ((r & USART_SR_ORE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
chopstx_intr_done (usartx_intr);
timeout = timeout_char;
r = chopstx_poll (&timeout, 1, ph);
if (r == 0)
break;
}
return len;
}

View File

@@ -203,11 +203,7 @@ rb_get_prepare_poll (struct rb *rb, chopstx_poll_cond_t *poll_desc)
const struct usart_stat *
usart_stat (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].stat;
}
@@ -215,11 +211,7 @@ usart_stat (uint8_t dev_no)
static struct USART *
get_usart_dev (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].USART;
}
@@ -227,11 +219,7 @@ get_usart_dev (uint8_t dev_no)
static struct rb *
get_usart_rb_h2a (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].rb_h2a;
}
@@ -239,11 +227,7 @@ get_usart_rb_h2a (uint8_t dev_no)
static struct rb *
get_usart_rb_a2h (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].rb_a2h;
}
@@ -251,11 +235,7 @@ get_usart_rb_a2h (uint8_t dev_no)
static struct chx_intr *
get_usart_intr (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].intr;
}

View File

@@ -1,101 +0,0 @@
/*
* usart-gd32vf103.c - USART driver for GD32VF103 (USART0)
*
* Copyright (C) 2017, 2019 g10 Code GmbH
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <mcu/stm32.h>
#include <contrib/usart.h>
#include <contrib/usart-impl.h>
#include <contrib/usart-impl-f103.h>
#define USART0_BASE (APB2PERIPH_BASE + 0x3800)
#define USART0 ((struct USART *)USART0_BASE)
static struct usart_stat usart0_stat;
static struct chx_intr usart0_intr;
#define BUF_A2H_SIZE 256
#define BUF_H2A_SIZE 512
static uint8_t buf_usart0_rb_a2h[BUF_A2H_SIZE];
static uint8_t buf_usart0_rb_h2a[BUF_H2A_SIZE];
static struct rb usart0_rb_a2h;
static struct rb usart0_rb_h2a;
static chopstx_poll_cond_t usart0_app_write_event;
/* Global variables so that it can be easier to debug. */
static int usart0_tx_ready;
#define INTR_REQ_USART0 56
#define USART_DEVNO_START 0
#define USART_DEVNO_END 0
static const struct usart usart_array[] =
{
{ USART0, &usart0_intr, INTR_REQ_USART0,
&usart0_stat, &usart0_rb_a2h, &usart0_rb_h2a, buf_usart0_rb_a2h,
buf_usart0_rb_h2a, &usart0_app_write_event, &usart0_tx_ready },
};
/* It's 96MHz f_PCLK */
static const struct brr_setting brr_table[] = {
/* Can't be represented in 16-bit { B600, (10000 << 4)}, */
/* Can't be represented in 16-bit { B1200, (5000 << 4)}, */
{ B2400, ( 2500 << 4)|0},
{ B9600, ( 625 << 4)|0},
{ B19200, ( 312 << 4)|8},
{ B57600, ( 104 << 4)|3},
{ B115200, ( 52 << 4)|1},
{ B230400, ( 26 << 4)|0},
{ B460800, ( 13 << 4)|0},
{ B921600, ( 6 << 4)|8},
{ BSCARD1, ( 620 << 4)|0}, /* 9677 */
{ BSCARD2, ( 310 << 4)|0}, /* 19354 */
{ BSCARD4, ( 155 << 4)|0}, /* 38709 */
{ BSCARD8, ( 77 << 4)|8}, /* 77419 */
{ BSCARD12, ( 51 << 4)|11}, /* 116129 */
{ BSCARD16, ( 38 << 4)|13}, /* 154506 */
{ BSCARD20, ( 31 << 4)|0}, /* 193548 */
};
#include "usart-common.c"
static void
usart_rcc_setup (void)
{
/* Enable USART0 clock, and strobe reset. */
RCC->APB2ENR |= (1 << 14);
RCC->APB2RSTR = (1 << 14);
RCC->APB2RSTR = 0;
}
#include "usart-common-f103.c"

View File

@@ -1,42 +0,0 @@
/* Hardware registers */
struct USART {
volatile uint32_t SR;
volatile uint32_t DR;
volatile uint32_t BRR;
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t CR3;
volatile uint32_t GTPR;
};
#define USART_SR_CTS (1 << 9)
#define USART_SR_LBD (1 << 8)
#define USART_SR_TXE (1 << 7)
#define USART_SR_TC (1 << 6)
#define USART_SR_RXNE (1 << 5)
#define USART_SR_IDLE (1 << 4)
#define USART_SR_ORE (1 << 3)
#define USART_SR_NE (1 << 2)
#define USART_SR_FE (1 << 1)
#define USART_SR_PE (1 << 0)
#define USART_CR1_UE (1 << 13)
#define USART_CR1_M (1 << 12)
#define USART_CR1_WAKE (1 << 11)
#define USART_CR1_PCE (1 << 10)
#define USART_CR1_PS (1 << 9)
#define USART_CR1_PEIE (1 << 8)
#define USART_CR1_TXEIE (1 << 7)
#define USART_CR1_TCIE (1 << 6)
#define USART_CR1_RXNEIE (1 << 5)
#define USART_CR1_IDLEIE (1 << 4)
#define USART_CR1_TE (1 << 3)
#define USART_CR1_RE (1 << 2)
#define USART_CR1_RWU (1 << 1)
#define USART_CR1_SBK (1 << 0)
#define USART_CR3_CTSE (1 << 9)
#define USART_CR3_RTSE (1 << 8)
#define USART_CR3_SCEN (1 << 5)
#define USART_CR3_NACK (1 << 4)

View File

@@ -1,25 +0,0 @@
struct usart {
struct USART *USART;
struct chx_intr *intr;
uint8_t irq_num;
struct usart_stat *stat;
struct rb *rb_a2h;
struct rb *rb_h2a;
uint8_t *buf_a2h;
uint8_t *buf_h2a;
chopstx_poll_cond_t *app_write_event;
int *tx_ready;
};
static int handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat);
static int handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat);
static void usart_config_recv_enable (struct USART *USARTx, int on);
struct brr_setting {
uint8_t baud_spec;
uint32_t brr_value;
};
#define NUM_BAUD (int)(sizeof (brr_table) / sizeof (struct brr_setting))
#define NUM_USART ((int)(sizeof (usart_array) / sizeof (struct usart)))

View File

@@ -31,14 +31,56 @@
#include <chopstx.h>
#include <mcu/stm32.h>
#include <contrib/usart.h>
#include <contrib/usart-impl.h>
#include <contrib/usart-impl-f103.h>
/* Hardware registers */
struct USART {
volatile uint32_t SR;
volatile uint32_t DR;
volatile uint32_t BRR;
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t CR3;
volatile uint32_t GTPR;
};
#define USART2_BASE (APB1PERIPH_BASE + 0x4400)
#define USART3_BASE (APB1PERIPH_BASE + 0x4800)
#define USART2 ((struct USART *)USART2_BASE)
#define USART3 ((struct USART *)USART3_BASE)
#define USART_SR_CTS (1 << 9)
#define USART_SR_LBD (1 << 8)
#define USART_SR_TXE (1 << 7)
#define USART_SR_TC (1 << 6)
#define USART_SR_RXNE (1 << 5)
#define USART_SR_IDLE (1 << 4)
#define USART_SR_ORE (1 << 3)
#define USART_SR_NE (1 << 2)
#define USART_SR_FE (1 << 1)
#define USART_SR_PE (1 << 0)
#define USART_CR1_UE (1 << 13)
#define USART_CR1_M (1 << 12)
#define USART_CR1_WAKE (1 << 11)
#define USART_CR1_PCE (1 << 10)
#define USART_CR1_PS (1 << 9)
#define USART_CR1_PEIE (1 << 8)
#define USART_CR1_TXEIE (1 << 7)
#define USART_CR1_TCIE (1 << 6)
#define USART_CR1_RXNEIE (1 << 5)
#define USART_CR1_IDLEIE (1 << 4)
#define USART_CR1_TE (1 << 3)
#define USART_CR1_RE (1 << 2)
#define USART_CR1_RWU (1 << 1)
#define USART_CR1_SBK (1 << 0)
#define USART_CR3_CTSE (1 << 9)
#define USART_CR3_RTSE (1 << 8)
#define USART_CR3_SCEN (1 << 5)
#define USART_CR3_NACK (1 << 4)
static struct usart_stat usart2_stat;
static struct usart_stat usart3_stat;
@@ -70,6 +112,20 @@ static int usart3_tx_ready;
#define USART_DEVNO_START 2
#define USART_DEVNO_END 3
struct usart {
struct USART *USART;
struct chx_intr *intr;
uint8_t irq_num;
struct usart_stat *stat;
struct rb *rb_a2h;
struct rb *rb_h2a;
uint8_t *buf_a2h;
uint8_t *buf_h2a;
chopstx_poll_cond_t *app_write_event;
int *tx_ready;
};
static const struct usart usart_array[] =
{
{ USART2, &usart2_intr, INTR_REQ_USART2,
@@ -79,6 +135,17 @@ static const struct usart usart_array[] =
&usart3_stat, &usart3_rb_a2h, &usart3_rb_h2a, buf_usart3_rb_a2h,
buf_usart3_rb_h2a, &usart3_app_write_event, &usart3_tx_ready },
};
#define NUM_USART ((int)(sizeof (usart_array) / sizeof (struct usart)))
static int handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat);
static int handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat);
static void usart_config_recv_enable (struct USART *USARTx, int on);
struct brr_setting {
uint8_t baud_spec;
uint32_t brr_value;
};
#define NUM_BAUD (int)(sizeof (brr_table) / sizeof (struct brr_setting))
/* We assume 36MHz f_PCLK */
static const struct brr_setting brr_table[] = {
@@ -102,14 +169,393 @@ static const struct brr_setting brr_table[] = {
};
#include "usart-common.c"
static void
usart_rcc_setup (void)
usart_config_recv_enable (struct USART *USARTx, int on)
{
if (on)
USARTx->CR1 |= USART_CR1_RE;
else
USARTx->CR1 &= ~USART_CR1_RE;
}
int
usart_config (uint8_t dev_no, uint32_t config_bits)
{
struct USART *USARTx = get_usart_dev (dev_no);
uint8_t baud_spec = (config_bits & MASK_BAUD);
int i;
uint32_t cr1_config = (USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE);
/* TXEIE/TCIE will be enabled when
putting char */
/* No CTSIE, PEIE, IDLEIE, LBDIE */
if (USARTx == NULL)
return -1;
/* Disable USART before configure. */
USARTx->CR1 &= ~USART_CR1_UE;
if (((config_bits & MASK_CS) == CS7 && (config_bits & PARENB))
|| ((config_bits & MASK_CS) == CS8 && (config_bits & PARENB) == 0))
cr1_config &= ~USART_CR1_M;
else if ((config_bits & MASK_CS) == CS8)
cr1_config |= USART_CR1_M;
else
return -1;
if ((config_bits & PARENB) == 0)
cr1_config &= ~(USART_CR1_PCE | USART_CR1_PEIE);
else
cr1_config |= (USART_CR1_PCE | USART_CR1_PEIE);
if ((config_bits & PARODD) == 0)
cr1_config &= ~USART_CR1_PS;
else
cr1_config |= USART_CR1_PS;
if ((config_bits & MASK_STOP) == STOP0B5)
USARTx->CR2 = (0x1 << 12);
else if ((config_bits & MASK_STOP) == STOP1B)
USARTx->CR2 = (0x0 << 12);
else if ((config_bits & MASK_STOP) == STOP1B5)
USARTx->CR2 = (0x3 << 12);
else /* if ((config_bits & MASK_STOP) == STOP2B) */
USARTx->CR2 = (0x2 << 12);
for (i = 0; i < NUM_BAUD; i++)
if (brr_table[i].baud_spec == baud_spec)
break;
if (i >= NUM_BAUD)
return -1;
USARTx->BRR = brr_table[i].brr_value;
if ((config_bits & MASK_FLOW))
USARTx->CR3 = USART_CR3_CTSE | USART_CR3_RTSE;
else
USARTx->CR3 = 0;
if (!(config_bits & MASK_MODE))
cr1_config |= USART_CR1_RE;
USARTx->CR1 = cr1_config;
/* SCEN (smartcard enable) should be set _after_ CR1. */
if ((config_bits & MASK_MODE))
{
if ((config_bits & MASK_MODE) == MODE_SMARTCARD)
{
USARTx->GTPR = (1 << 8) | 5;
USARTx->CR3 |= (USART_CR3_SCEN | USART_CR3_NACK);
}
else if ((config_bits & MASK_MODE) == MODE_IRDA)
USARTx->CR3 |= (1 << 1);
else if ((config_bits & MASK_MODE) == MODE_IRDA_LP)
USARTx->CR3 |= (1 << 2) | (1 << 1);
}
return 0;
}
void
usart_init0 (int (*cb) (uint8_t dev_no, uint16_t notify_bits))
{
int i;
ss_notify_callback = cb;
for (i = 0; i < NUM_USART; i++)
{
if (usart_array[i].stat)
usart_array[i].stat->dev_no = i + USART_DEVNO_START;
chopstx_claim_irq (usart_array[i].intr, usart_array[i].irq_num);
}
/* Enable USART2 and USART3 clocks, and strobe reset. */
RCC->APB1ENR |= ((1 << 18) | (1 << 17));
RCC->APB1RSTR = ((1 << 18) | (1 << 17));
RCC->APB1RSTR = 0;
}
#define UART_STATE_BITMAP_RX_CARRIER (1 << 0)
#define UART_STATE_BITMAP_TX_CARRIER (1 << 1)
#define UART_STATE_BITMAP_BREAK (1 << 2)
#define UART_STATE_BITMAP_RINGSIGNAL (1 << 3)
#define UART_STATE_BITMAP_FRAMING (1 << 4)
#define UART_STATE_BITMAP_PARITY (1 << 5)
#define UART_STATE_BITMAP_OVERRUN (1 << 6)
#include "usart-common-f103.c"
static int
handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
{
int tx_ready = 0;
uint32_t r = USARTx->SR;
int notify_bits = 0;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
if (smartcard_mode)
{
if ((r & USART_SR_TC))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TCIE;
}
}
else
{
if ((r & USART_SR_TXE))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TXEIE;
}
}
if ((r & USART_SR_RXNE))
{
uint32_t data = USARTx->DR;
/* DR register should be accessed even if data is not used.
* Its read-access has side effect of clearing error flags.
*/
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_SR_NE))
stat->err_rx_noise++;
else if ((r & USART_SR_FE))
{
/* NOTE: Noway to distinguish framing error and break */
stat->rx_break++;
notify_bits |= UART_STATE_BITMAP_BREAK;
}
else if ((r & USART_SR_PE))
{
stat->err_rx_parity++;
notify_bits |= UART_STATE_BITMAP_PARITY;
}
else
{
if ((r & USART_SR_ORE))
{
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
/* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */
if (rb_ll_put (rb2a, (data & 0xff)) < 0)
stat->err_rx_overflow++;
else
stat->rx++;
}
}
else if ((r & USART_SR_ORE))
{ /* Clear ORE */
uint32_t data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
if (notify_bits)
{
if (ss_notify_callback
&& (*ss_notify_callback) (stat->dev_no, notify_bits))
stat->err_notify_overflow++;
}
return tx_ready;
}
static int
handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat)
{
int tx_ready = 1;
int c = rb_ll_get (rb2h);
if (c >= 0)
{
uint32_t r;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
USARTx->DR = (c & 0xff);
stat->tx++;
r = USARTx->SR;
if (smartcard_mode)
{
if ((r & USART_SR_TC) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TCIE;
}
}
else
{
if ((r & USART_SR_TXE) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TXEIE;
}
}
}
return tx_ready;
}
int
usart_send_break (uint8_t dev_no)
{
struct USART *USARTx = get_usart_dev (dev_no);
if (USARTx == NULL)
return -1;
if ((USARTx->CR1 & 0x01))
return 1; /* Busy sending break, which was requested before. */
USARTx->CR1 |= 0x01;
return 0;
}
int
usart_block_sendrecv (uint8_t dev_no, const char *s_buf, uint16_t s_buflen,
char *r_buf, uint16_t r_buflen,
uint32_t *timeout_block_p, uint32_t timeout_char)
{
uint32_t timeout;
uint8_t *p;
int len;
uint32_t r;
uint32_t data;
struct USART *USARTx = get_usart_dev (dev_no);
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
struct chx_intr *usartx_intr = get_usart_intr (dev_no);
struct chx_poll_head *ph[1];
if (usartx_intr == NULL)
return -1;
ph[0] = (struct chx_poll_head *)usartx_intr;
p = (uint8_t *)s_buf;
if (p)
{
if (smartcard_mode)
usart_config_recv_enable (USARTx, 0);
USARTx->CR1 |= USART_CR1_TXEIE;
/* Sending part */
while (1)
{
chopstx_poll (NULL, 1, ph);
r = USARTx->SR;
/* Here, ignore recv error(s). */
if ((r & USART_SR_RXNE) || (r & USART_SR_ORE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
if ((r & USART_SR_TXE))
{
if (s_buflen == 0)
break;
else
{
/* Keep TXEIE bit */
USARTx->DR = *p++;
s_buflen--;
}
}
chopstx_intr_done (usartx_intr);
}
USARTx->CR1 &= ~USART_CR1_TXEIE;
if (smartcard_mode)
{
if (timeout_block_p && (*timeout_block_p))
do
r = USARTx->SR;
while (((r & USART_SR_TC) == 0));
usart_config_recv_enable (USARTx, 1);
if (timeout_block_p && *timeout_block_p == 0)
{
/* Ignoring the echo back. */
do
r = USARTx->SR;
while (((r & USART_SR_TC) == 0));
if ((r & USART_SR_RXNE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
*timeout_block_p = timeout_char;
}
}
chopstx_intr_done (usartx_intr);
}
if (r_buf == NULL)
return 0;
if (!p)
if (smartcard_mode)
usart_config_recv_enable (USARTx, 1);
/* Receiving part */
r = chopstx_poll (timeout_block_p, 1, ph);
if (r == 0)
return 0;
p = (uint8_t *)r_buf;
len = 0;
while (1)
{
r = USARTx->SR;
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_SR_RXNE))
{
if ((r & USART_SR_NE) || (r & USART_SR_FE) || (r & USART_SR_PE))
/* ignore error, for now. XXX: ss_notify */
;
else
{
*p++ = (data & 0xff);
len++;
r_buflen--;
if (r_buflen == 0)
{
chopstx_intr_done (usartx_intr);
break;
}
}
}
else if ((r & USART_SR_ORE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
chopstx_intr_done (usartx_intr);
timeout = timeout_char;
r = chopstx_poll (&timeout, 1, ph);
if (r == 0)
break;
}
return len;
}

View File

@@ -3,7 +3,7 @@
#include <chopstx.h>
#include <mcu/stm32l.h>
#include <contrib/usart.h>
#include <contrib/usart-impl.h>
#define RCC_APB1_1_USART2 (1 << 17)
#define RCC_APB2_USART1 (1 << 14)
@@ -84,6 +84,20 @@ static int usart2_tx_ready;
#define USART_DEVNO_START 1
#define USART_DEVNO_END 2
struct usart {
struct USART *USART;
struct chx_intr *intr;
uint8_t irq_num;
struct usart_stat *stat;
struct rb *rb_a2h;
struct rb *rb_h2a;
uint8_t *buf_a2h;
uint8_t *buf_h2a;
chopstx_poll_cond_t *app_write_event;
int *tx_ready;
};
static const struct usart usart_array[] =
{
{ USART1, &usart1_intr, INTR_REQ_USART1,
@@ -95,6 +109,17 @@ static const struct usart usart_array[] =
buf_usart2_rb_h2a, &usart2_app_write_event, &usart2_tx_ready,
},
};
#define NUM_USART ((int)(sizeof (usart_array) / sizeof (struct usart)))
static int handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat);
static int handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat);
static void usart_config_recv_enable (struct USART *USARTx, int on);
struct brr_setting {
uint8_t baud_spec;
uint32_t brr_value;
};
#define NUM_BAUD (int)(sizeof (brr_table) / sizeof (struct brr_setting))
/* We assume 40MHz f_CK */
static const struct brr_setting brr_table[] = {

View File

@@ -230,10 +230,3 @@ clock.
Return previous value of @var{enable_sleep}.
@end deftypefun
@subheading chopstx_critical
@anchor{chopstx_critical}
@deftypefun {void *} {chopstx_critical} (void *func (void * @var{})
Run the function @var{func} with @var{arg} under CPU schedule lock.
Return void * pointer.
@end deftypefun

View File

@@ -1,7 +1,7 @@
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename chopstx.info
@set VERSION 2.0
@set VERSION 1.21
@settitle Chopstx Reference Manual
@c Unify some of the indices.
@syncodeindex tp fn
@@ -11,7 +11,7 @@
This manual is for Chopstx (version @value{VERSION}).
@noindent
Copyright @copyright{} 2013, 2015, 2016, 2017, 2018, 2019, 2020 Flying Stone Technology @*
Copyright @copyright{} 2013, 2015, 2016, 2017, 2018, 2019 Flying Stone Technology @*
@quotation
Permission is granted to copy, distribute and/or modify this document
@@ -89,10 +89,9 @@ Indexes
@chapter Introduction
Chopstx is an RT thread library for ARM Cortex-M0, Cortex-M0plus,
Cortex-M3, Cortex-M4 with no FPU or DSP, RISC-V Bumblebee, and
GNU/Linux emulation. Specifically, it is used for STM32F030, MKL27Z,
STM32F103, GD32F103, STM32L432, GD32V103 and as a command on
GNU/Linux.
Cortex-M3, Cortex-M4 with no FPU or DSP, and GNU/Linux emulation.
Specifically, it is used for STM32F030, MKL27Z, STM32F103, GD32F103,
STM32L432 and as a command on GNU/Linux.
While most RTOSes come with many features, drivers, and stacks,
Chopstx just offers an RT thread library.

View File

@@ -1,84 +0,0 @@
/*
* entry-riscv32.c - Entry routine when reset.
*
* Copyright (C) 2019
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include "board.h"
#include "mcu/clk_gpio_init-gd32vf103.c"
/*
* Startup entry point.
*/
void __attribute__ ((naked,section(".text.startup.0")))
entry (void)
{
/* Start at 0x00000000 (alias 0x08000000), interrupt masked */
asm volatile (
"li a0,0x0800000c\n\t"
"jr a0\n\t" /* Jump to physical address */
".align 2\n\t"
".option push\n"
".option norelax\n\t"
"la gp,__global_pointer$\n"
".option pop\n\t"
"la sp,__main_stack_end__\n\t"
"call clock_init\n\t"
/* Clear BSS section. */
"la a0,_bss_start\n\t"
"la a1,_bss_end\n\t"
"0:\n\t"
"beq a0,a1,1f\n"
"sw zero,(a0)\n\t"
"addi a0,a0,4\n\t"
"j 0b\n"
"1:\n\t"
/* Copy data section. */
"la a0,_textdata\n\t"
"la a1,_data\n\t"
"la a2,_edata\n"
"0:\n\t"
"beq a1,a2,1f\n\t"
"lw t0,(a0)\n\t"
"sw t0,(a1)\n\t"
"addi a0,a0,4\n\t"
"addi a1,a1,4\n\t"
"j 0b\n"
"1:\n\t"
/* Switch to application stack. */
"la sp,__process0_stack_end__-160\n\t"
"mv a0,sp\n\t"
"call chx_init\n\t"
"call chx_systick_init\n\t"
"call gpio_init\n\t"
/* Enable interrupts. */
"csrsi mstatus,8\n\t"
/* Call main. */
"call main\n\t"
"3:\n\t"
"j 3b"
: /* no output */ : /* no input */ : "memory");
}

View File

@@ -1,5 +1,5 @@
/*
* entry-cortex-m.c - Entry routine when reset and interrupt vectors.
* entry.c - Entry routine when reset and interrupt vectors.
*
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2019
* Flying Stone Technology
@@ -64,24 +64,8 @@ extern uint8_t __main_stack_end__;
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
extern void svc (void);
#endif
/*
* In ARMv6-M Architecture Reference Manual and ARM v7-M Architecture
* Reference Manual, you can find a section B1.5.12 for tail-chaining.
*
* B1.5.12 Exceptions on exception return, and tail-chaining exceptions
*/
/*
* Because it is tail-chained, the preempt function has an argument
* with type of (struct chx_thread *), in fact.
*/
extern void preempt (void);
/*
* Following functions return type of (struct chx_thread *) for
* tail-chained function (the preempt function), for its argument.
*/
extern void chx_timer_handler (void);
extern void chx_timer_expired (void);
extern void chx_handle_intr (void);
static void nmi (void)
@@ -209,7 +193,7 @@ handler vector_table[] __attribute__ ((section(".startup.vectors"))) = {
none, /* Debug */
none, /* reserved */
preempt, /* PendSV */
chx_timer_handler, /* SysTick */
chx_timer_expired, /* SysTick */
/* 0x40 */
chx_handle_intr /* WWDG */, chx_handle_intr /* PVD */,
chx_handle_intr /* TAMPER */, chx_handle_intr /* RTC */,

View File

@@ -8,7 +8,6 @@ CHOPSTX = ..
LDSCRIPT=
CSRC = sample.c usb-cdc.c command.c
ARCH=gnu-linux
CHIP=gnu-linux
USE_SYS = yes
USE_USB = yes

View File

@@ -179,7 +179,14 @@ main (int argc, const char *argv[])
if (size < 0)
goto connection_loop;
if (size == 1)
if (size == 0)
/* Timeout */
{
if (tty_send (tty, "\r\n", 2) < 0)
return 0;
break;
}
else if (size == 1)
/* Do nothing but prompt again. */
break;
else if (size)

View File

@@ -904,8 +904,18 @@ tty_recv (struct tty *t, char *buf, uint32_t *timeout)
chopstx_mutex_lock (&t->mtx);
r = check_rx (t);
chopstx_mutex_unlock (&t->mtx);
if (r || (timeout != NULL && *timeout == 0))
if (r)
break;
else if (timeout != NULL && *timeout == 0)
{
int i;
/* Cancel the input. */
for (i = 0; i < t->inputline_len; i++)
tty_send (t, "\x08\x20\x08", 3);
t->inputline_len = 0;
break;
}
}
chopstx_mutex_lock (&t->mtx);

View File

@@ -6,7 +6,6 @@ CHOPSTX = ..
LDSCRIPT= sample.ld.m4
CSRC = sample.c usb-cdc.c
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes

View File

@@ -8,7 +8,6 @@ FRAUCHEKY = ../../fraucheky
LDSCRIPT=
CSRC = main.c
ARCH=gnu-linux
CHIP=gnu-linux
USE_SYS = yes
USE_USB = yes

View File

@@ -7,7 +7,6 @@ PROJECT = sample
CHOPSTX = ..
LDSCRIPT= sample.ld
CSRC = sample.c usb-cdc.c command.c touch.c
ARCH=cortex-m
CHIP=mkl27z
USE_SYS = yes

View File

@@ -7,7 +7,6 @@ LDSCRIPT= hacker-emblem.ld
CSRC = reset.c hh.c
ARCH=cortex-m
CHIP=stm32f0
# Hacker Emblem and "Happy Hacking!" demonstration

View File

@@ -32,7 +32,7 @@ reset (void)
extern uint8_t __main_stack_end__;
extern void preempt (void);
extern void chx_timer_handler (void);
extern void chx_timer_expired (void);
extern void chx_handle_intr (void);
static void nmi (void)
@@ -85,7 +85,7 @@ handler vector[] __attribute__ ((section(".vectors"))) = {
none, /* Debug */
none, /* reserved */
preempt, /* PendSV */
chx_timer_handler, /* SysTick */
chx_timer_expired, /* SysTick */
/* 0x40 */
chx_handle_intr /* WWDG */, chx_handle_intr /* PVD */,
chx_handle_intr /* TAMPER */, chx_handle_intr /* RTC */,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -1,59 +0,0 @@
# Makefile for example application of Chopstx
PROJECT = sample
### Currently, it is for GD32VF103
###
### Please change lines started with '###' for STM32 Nucleo L432.
###
### Please change lines started with '###' for Cortex-M3 board.
###
### Please change lines started with '###' for Cortex-M0+ board
### (STM32F0 Discovery).
CHOPSTX = ..
LDSCRIPT= sample.ld.gd32vf103
### LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld
### LDSCRIPT= sample.ld.m3
CSRC = sample.c usb-vendor-specific.c
# CSRC += debug.c
ARCH=riscv32
CHIP=gd32vf103
### CHIP=stm32l4
### USE_SYS = yes
USE_USART = yes
USE_USB = yes
###################################
### CROSS = arm-none-eabi-
CROSS = riscv64-unknown-elf-
CC = $(CROSS)gcc
LD = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
### MCU = cortex-m4
### MCU = cortex-m3
### MCU = cortex-m0
CWARN = -Wall -Wextra -Wstrict-prototypes
DEFS = -DFREE_STANDING -DMHZ=96
### DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=80
### DEFS = -DFREE_STANDING -DUSE_SYS3 -DBUSY_LOOP -DCHX_FLAGS_MAIN=CHOPSTX_SCHED_RR
OPT = -O3 -Os -g
LIBS =
#LIBS = -lgcc
CSRC += $(CHOPSTX)/contrib/spi-st.c
####################
include ../rules.mk
board.h:
@echo Please make a symbolic link \'board.h\' to a file in ../board;
@exit 1
sys.c: board.h
distclean: clean
rm -f board.h

View File

@@ -1 +0,0 @@
../board/board-longan-nano.h

View File

@@ -1,34 +0,0 @@
from load_image_to_screen import Longan_LCD
from gnupg_logo_16bit_160x80 import gnupg_logo_16bit_160x80
image = gnupg_logo_16bit_160x80
pos = [ 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 14, 17, 20, 23, 28, 33, 40, 47, 56 ]
background0 = [ 0x5d, 0xdf ]
background1 = [ 0x7e, 0xdf ]
def fill(i, bg0, bg1):
if (i % 2) == 0:
return bg0
else:
return bg1
def move_right(x, bg0, bg1, img):
result = []
for i in range(80):
for j in range(x):
result = result + fill(j, bg0, bg1)
result = result + (img[i*160*2:(i+1)*160*2])[0:160*2-x*2]
return result
images = [ bytes(move_right(x, background0, background1, image)) for x in pos ]
lcd = Longan_LCD()
for loop in range(10):
for i in range(len(pos)):
lcd.load_image_to_screen(images[len(pos)-1-i])
for i in range(len(pos)):
lcd.load_image_to_screen(images[i])

View File

@@ -1,26 +0,0 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <contrib/usart.h>
#include <stdarg.h>
extern int vsnprintf(char *str, size_t size, const char *format, va_list ap);
#define USART_NO 0
/* Intentionally only adding LF */
void
debug (const char *fmt, ...)
{
char buf[256];
va_list ap;
size_t len;
va_start(ap, fmt);
len = vsnprintf (buf, sizeof buf - 1, fmt, ap);
va_end(ap);
if (len >= sizeof buf - 1)
len = sizeof buf - 2;
buf[len++] = '\n';
usart_write (USART_NO, buf, len);
}

View File

@@ -1 +0,0 @@
void debug (const char *fmt, ...);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,96 +0,0 @@
#! /usr/bin/python3
# pos = [
# 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
# 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 11,
# 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 28, 30, 31, 33, 35,
# 37, 40, 42, 44, 47, 50, 53, 56, 59, 63, 66, 70, 75, 79, 84, 89, 94,100,105,112,
# 118,125,133,141,149,158
# ]
pos = [
0, 1, 1, 1,
1, 2, 2, 3,
4, 6, 8, 11,
15, 20, 26, 35,
47, 63, 84, 112,
149
]
import usb.core
import usb.util
dev = usb.core.find(idVendor=0xffff, idProduct=0x0001)
if dev is None:
raise ValueError('Device not found')
# dev.set_configuration()
# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
ep_out = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
ep_in = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
if ep_out is None:
raise ValueError('Endpoint to send data not found')
if ep_in is None:
raise ValueError('Endpoint to receive data not found')
color_black = [ 0, 0 ]
color0 = [ 0xff, 0x03 ]
color1 = [ 0xef, 0x7f ]
color2 = [ 0x00, 0x78 ]
color3 = [ 0xff, 0xff ]
def gen_image(x):
image = []
for i in range(80):
image = image + (color_black * x) + (color0 * (160-x))
return image
images = [ gen_image(p) for p in pos ]
# images = [ gen_image(p) for p in range(160) ]
import time
import sys
def prepare_send():
w=ep_in.read(64)
# print(w)
def finish_send():
dev.ctrl_transfer(0x41, 0x00, 0x00, 0x00, "")
# print(".", end='', flush=True)
for i in range(len(images)*8):
i0 = i % (len(images)*2)
if i0 < len(images):
x = (i % len(images))
else:
x = len(images)- 1 - (i % len(images))
# print("%d %d: %d" % (i, x, len(images[x])))
if True:
prepare_send()
ep_out.write(images[x])
finish_send()
prepare_send()
dev.ctrl_transfer(0x41, 0x00, 0x01, 0x00, "")

View File

@@ -1,29 +0,0 @@
l = [ 0b00000_000000_00000,
0b00000_000000_01111,
0b00000_000000_11111,
0b00000_011111_00000,
0b00000_011111_01111,
0b00000_011111_11111,
0b00000_111111_00000,
0b00000_111111_01111,
0b00000_111111_11111,
0b01111_000000_00000,
0b01111_000000_01111,
0b01111_000000_11111,
0b01111_011111_00000,
0b01111_011111_01111,
0b01111_011111_11111,
0b01111_111111_00000,
0b01111_111111_01111,
0b01111_111111_11111,
0b11111_000000_00000,
0b11111_000000_01111,
0b11111_000000_11111,
0b11111_011111_00000,
0b11111_011111_01111,
0b11111_011111_11111,
0b11111_111111_00000,
0b11111_111111_01111,
0b11111_111111_11111 ]
for color in l:
print("0x%04x," % color)

View File

@@ -1,51 +0,0 @@
#! /usr/bin/python3
import usb.core
import usb.util
import time
import sys
class Longan_LCD(object):
def __init__(self):
self.dev = usb.core.find(idVendor=0xffff, idProduct=0x0001)
if self.dev is None:
raise ValueError('Device not found')
cfg = self.dev.get_active_configuration()
intf = cfg[(0,0)]
self.ep_out = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
self.ep_in = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
if self.ep_out is None:
raise ValueError('Endpoint to send data not found')
if self.ep_in is None:
raise ValueError('Endpoint to receive data not found')
def prepare_send(self):
w=self.ep_in.read(64)
# print(w)
def finish_send(self):
self.dev.ctrl_transfer(0x41, 0x00, 0x00, 0x00, "")
# print(".", end='', flush=True)
def load_image_to_screen(self, image):
self.prepare_send()
self.ep_out.write(image)
self.finish_send()

View File

@@ -1,93 +0,0 @@
#! /usr/bin/python3
import usb.core
import usb.util
dev = usb.core.find(idVendor=0xffff, idProduct=0x0001)
if dev is None:
raise ValueError('Device not found')
# dev.set_configuration()
# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
ep_out = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
ep_in = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
if ep_out is None:
raise ValueError('Endpoint to send data not found')
if ep_in is None:
raise ValueError('Endpoint to receive data not found')
def prepare_send():
w=ep_in.read(64)
# print(w)
def finish_send():
dev.ctrl_transfer(0x41, 0x00, 0x00, 0x00, "")
# print(".", end='', flush=True)
color0 = [ 0xff, 0x03 ]
color1 = [ 0xef, 0x7f ]
color2 = [ 0x00, 0x78 ]
color3 = [ 0xff, 0xff ]
image = color0*160*80
# + color1*160*40
image64 = color0*32
image = color1*160*80
point1600a=(color0*40+color1*40)*20
point1600b=(color1*40+color0*40)*20
point1600c=(color2*20+color3*20)*40
point1600d=(color3*20+color2*20)*40
import time
import sys
for i in range(10):
prepare_send()
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".", end='', flush=True)
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".", end='', flush=True)
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".", end='', flush=True)
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".")
finish_send()
prepare_send()
ep_out.write(point1600c*2+point1600d*2+point1600c*2+point1600d*2)
print(".")
finish_send()
prepare_send()
dev.ctrl_transfer(0x41, 0x00, 0x01, 0x00, "")

View File

@@ -1,373 +0,0 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include "board.h"
#include <contrib/spi.h>
#include <contrib/usart.h>
#include <mcu/gd32vf103.h>
#include "vrsp.h"
void
set_led_b (void)
{
GPIOA->ODR |= (1 << 1);
GPIOA->ODR &= ~(1 << 2);
GPIOC->ODR |= (1 << 13);
}
void
set_led_g (void)
{
GPIOA->ODR &= ~(1 << 1);
GPIOA->ODR |= (1 << 2);
GPIOC->ODR |= (1 << 13);
}
void
set_led (int on)
{
if (on)
GPIOC->ODR &= ~(1 << 13);
else
GPIOC->ODR |= (1 << 13);
}
#define STACK_MAIN
#define STACK_PROCESS_1
#include "stack-def.h"
#define PRIO_USART 3
#define STACK_ADDR_USART ((uint32_t)process1_base)
#define STACK_SIZE_USART (sizeof process1_base)
#define LCD_WIDTH 160
#define LCD_HEIGHT 80
/*
* GPIO PB1: RES#
* low active
*/
static void
lcd_reset (int assert)
{
if (assert)
GPIOB->ODR &= ~(1 << 1);
else
GPIOB->ODR |= (1 << 1);
}
/*
* GPIO PB2: CS#
* low active
*/
static void
lcd_chip_select (int assert)
{
if (assert)
GPIOB->ODR &= ~(1 << 2);
else
GPIOB->ODR |= (1 << 2);
}
/*
* GPIO PB0: D/C#
* Display: 1
* Command: 0
*/
#define LCD_DISPLAY 1
#define LCD_COMMAND 0
static void
lcd_control (int control)
{
if (control == LCD_COMMAND)
GPIOB->ODR &= ~(1 << 0);
else
GPIOB->ODR |= (1 << 0);
}
static void
lcd_send (uint8_t data)
{
spi_send (data);
spi_recv ();
}
static void
lcd_data8 (uint8_t data)
{
lcd_send (data);
}
static void
lcd_data16 (uint16_t data)
{
lcd_send (data>>8);
lcd_send (data);
}
static void
lcd_command (uint8_t cmd)
{
lcd_control (LCD_COMMAND);
lcd_send (cmd);
lcd_control (LCD_DISPLAY);
}
/* LCD: ST7735S */
static void
lcd_init (void)
{
spi_init ();
lcd_reset (1);
chopstx_usec_wait (500*1000);
lcd_reset (0);
lcd_chip_select (1);
chopstx_usec_wait (100*1000);
lcd_command (0x11); /* SLPOUT: Sleep out */
chopstx_usec_wait (100*1000);
lcd_command (0x21); /* INVON: display inversion on */
/* INVOFF: display inversion off: 0x20 */
lcd_command (0x3a); /* COLMOD: Pixel data format */
lcd_data8 (0x05); /* 16-bit/pixel */
lcd_command (0x36); /* MADCTL: Memory Data Access Control*/
lcd_data8 (0x78);
lcd_command (0xb1); /* FRMCTR1: frame rate control 1 */
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_command (0xb2); /* FRMCTR2: frame rate control 2 */
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_command (0xb3); /* FRMCTR3: frame rate control 3 */
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_command (0xb4); /* INVCTR: inversion control */
lcd_data8 (0x03);
lcd_command (0xc0); /* PWCTR1: power control 1 */
lcd_data8 (0x62);
lcd_data8 (0x02);
lcd_data8 (0x04);
lcd_command (0xc1); /* PWCTR1: power control 2 */
lcd_data8 (0xc0);
lcd_command (0xc2); /* PWCTR1: power control 3 */
lcd_data8 (0x0d);
lcd_data8 (0x00);
lcd_command (0xc3); /* PWCTR1: power control 4 */
lcd_data8 (0x8d);
lcd_data8 (0x6a);
lcd_command (0xc4); /* PWCTR1: power control 5 */
lcd_data8 (0x8d);
lcd_data8 (0xee);
lcd_command (0xc5); /* VCMCTR1: VCOM control 1 */
lcd_data8 (0x0e);
lcd_command (0xe0); /* GMCTRP1: Gamma correction setting */
lcd_data8 (0x10);
lcd_data8 (0x0e);
lcd_data8 (0x02);
lcd_data8 (0x03);
lcd_data8 (0x0e);
lcd_data8 (0x07);
lcd_data8 (0x02);
lcd_data8 (0x07);
lcd_data8 (0x0a);
lcd_data8 (0x12);
lcd_data8 (0x27);
lcd_data8 (0x37);
lcd_data8 (0x00);
lcd_data8 (0x0d);
lcd_data8 (0x0e);
lcd_data8 (0x10);
lcd_command (0xe1); /* GMCTRN1: Gamma correction setting */
lcd_data8 (0x10);
lcd_data8 (0x0e);
lcd_data8 (0x03);
lcd_data8 (0x03);
lcd_data8 (0x0f);
lcd_data8 (0x06);
lcd_data8 (0x02);
lcd_data8 (0x08);
lcd_data8 (0x0a);
lcd_data8 (0x13);
lcd_data8 (0x26);
lcd_data8 (0x36);
lcd_data8 (0x00);
lcd_data8 (0x0d);
lcd_data8 (0x0e);
lcd_data8 (0x10);
/* All set up! Now, turn on the display. */
lcd_command (0x29); /* DISPON: Display On */
}
static void
lcd_address (uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
lcd_command (0x2a); /* CASET: Column address */
lcd_data16 (x1+1);
lcd_data16 (x2+1);
lcd_command (0x2b); /* RASET: Row address */
lcd_data16 (y1+26);
lcd_data16 (y2+26);
lcd_command (0x2c); /* RAMWR: memory write */
}
static void
lcd_clear (uint16_t color)
{
uint16_t i, j;
lcd_address (0, 0, LCD_WIDTH-1, LCD_HEIGHT-1);
for (i = 0; i < LCD_WIDTH; i++)
for (j = 0; j < LCD_HEIGHT; j++)
lcd_data16 (color);
}
static void
lcd_load_image (const uint16_t *image)
{
int i;
lcd_address (0, 0, LCD_WIDTH-1, LCD_HEIGHT-1);
for(i = 0; i < LCD_WIDTH*LCD_HEIGHT; i++)
lcd_data16 (image[i]);
}
/* Generated with list_565_colors.py */
static uint16_t colors[27] =
{
0x0000, 0x000f, 0x001f, 0x03e0, 0x03ef, 0x03ff, 0x07e0, 0x07ef, 0x07ff,
0x7800, 0x780f, 0x781f, 0x7be0, 0x7bef, 0x7bff, 0x7fe0, 0x7fef, 0x7fff,
0xf800, 0xf80f, 0xf81f, 0xfbe0, 0xfbef, 0xfbff, 0xffe0, 0xffef, 0xffff,
};
static int
ss_notify (uint8_t dev_no, uint16_t state_bits)
{
(void)dev_no;
(void)state_bits;
return 0;
}
uint16_t screen_image[12800];
int
main (int argc, const char *argv[])
{
chopstx_poll_cond_t usart_poll_desc;
chopstx_poll_cond_t usb_poll_desc;
struct chx_poll_head *ph[2];
uint32_t timeout;
int cl = 0;
int play_mode = 0;
struct vrsp *v;
uint32_t u = 0;
(void)argc;
(void)argv;
usart_init (PRIO_USART, STACK_ADDR_USART, STACK_SIZE_USART, ss_notify);
usart_config (0, B115200 | CS8 | STOP1B);
v = vrsp_open ();
chopstx_usec_wait (500*1000);
set_led (1);
lcd_init ();
lcd_clear (colors[26]);
chopstx_usec_wait (500*1000);
vrsp_prepare_poll (v, &usb_poll_desc);
ph[0] = (struct chx_poll_head *)&usb_poll_desc;
usart_read_prepare_poll (0, &usart_poll_desc);
ph[1] = (struct chx_poll_head *)&usart_poll_desc;
timeout = 200*1000*6;
while (1)
{
chopstx_poll (&timeout, 2, ph);
if (usart_poll_desc.ready)
{
char buf[16];
int r;
r = usart_read (0, buf, 16);
if (r)
usart_write (0, buf, r);
}
if (usb_poll_desc.ready)
{
int r;
r = vrsp_screen_acquire (v);
if (r < 0)
/* It's busy. */
;
else if (r == 0)
{
play_mode = 1;
lcd_load_image (screen_image);
vrsp_screen_release (v);
}
else
{
play_mode = 0;
vrsp_screen_release (v);
}
}
if (timeout == 0)
{
usart_write (0, "Hello\r\n", 7);
timeout = 200*1000*6;
u ^= 1;
set_led (u);
if (play_mode == 0)
{
lcd_clear (colors[cl]);
cl++;
if (cl >= 27)
cl = 0;
}
}
}
return 0;
}

View File

@@ -1,92 +0,0 @@
/*
* GD32VF103 memory setup.
*/
MEMORY
{
flash : org = 0x08000000, len = 128k
ram : org = 0x20000000, len = 32k
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = 32k;
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
_text = .;
.text :
{
*(.text.startup.0)
*(.text.startup.1)
*(.text.startup.*)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.rodata)
*(.rodata.*)
. = ALIGN(4);
} > flash
PROVIDE (_etext = .);
_textdata = _etext;
.stacks (NOLOAD) :
{
. = ALIGN(8);
*(.main_stack)
*(.process_stack.0)
*(.process_stack.1)
*(.process_stack.2)
*(.process_stack.3)
*(.process_stack.4)
. = ALIGN(8);
} > ram
.data :
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.gnu.linkonce.r.*)
*(.data)
. = ALIGN(4);
*(.data.*)
*(.gnu.linkonce.d.*)
. = ALIGN(8);
PROVIDE( __global_pointer$ = . + 0x800);
*(.sdata .sdata.*)
*(.gnu.linkonce.s.*)
. = ALIGN(8);
*(.srodata.cst16)
*(.srodata.cst8)
*(.srodata.cst4)
*(.srodata.cst2)
*(.srodata .srodata.*)
} > ram AT > flash
. = ALIGN(4);
PROVIDE(_edata = .);
PROVIDE(_bss_start = .);
.bss :
{
*(.sbss*)
*(.gnu.linkonce.sb.*)
*(.bss .bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
} > ram
. = ALIGN(8);
PROVIDE(_bss_end = .);
PROVIDE(end = .);
PROVIDE(_end = .);
}
__heap_base__ = _end;
__heap_end__ = __ram_end__;

View File

@@ -1,55 +0,0 @@
#define MAIN_SIZE 0x0100 /* Idle+Exception handlers */
#define SIZE_0 0x0600 /* Main program */
#define SIZE_1 0x0a00 /* first thread program */
#define SIZE_2 0x0600 /* second thread program */
#define SIZE_3 0x0000 /* third thread program */
#define SIZE_4 0x0000 /* fourth thread program */
#if defined(STACK_MAIN)
/*
* The terminology of "main" is confusing in ARM architecture.
* Here, "main_base" is for exception handlers.
*/
/* Idle+Exception handlers */
char __main_stack_end__[0] __attribute__ ((section(".main_stack")));
char main_base[MAIN_SIZE] __attribute__ ((section(".main_stack")));
/* Main program */
char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0")));
char process0_base[SIZE_0] __attribute__ ((section(".process_stack.0")));
#endif
/* First thread program */
#if defined(STACK_PROCESS_1)
char process1_base[SIZE_1] __attribute__ ((section(".process_stack.1")));
#endif
/* Second thread program */
#if defined(STACK_PROCESS_2)
char process2_base[SIZE_2] __attribute__ ((section(".process_stack.2")));
#endif
/* Third thread program */
#if defined(STACK_PROCESS_3)
char process3_base[SIZE_3] __attribute__ ((section(".process_stack.3")));
#endif
/* Fourth thread program */
#if defined(STACK_PROCESS_4)
char process4_base[SIZE_4] __attribute__ ((section(".process_stack.4")));
#endif
/* Fifth thread program */
#if defined(STACK_PROCESS_5)
char process5_base[SIZE_5] __attribute__ ((section(".process_stack.5")));
#endif
/* Sixth thread program */
#if defined(STACK_PROCESS_6)
char process6_base[SIZE_6] __attribute__ ((section(".process_stack.6")));
#endif
/* Seventh thread program */
#if defined(STACK_PROCESS_7)
char process7_base[SIZE_7] __attribute__ ((section(".process_stack.7")));
#endif

View File

@@ -1,585 +0,0 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <string.h>
#include "board.h"
#include "usb_lld.h"
#include "vrsp.h"
extern uint16_t screen_image[12800];
static chopstx_intr_t usb_intr;
struct vrsp {
struct usb_dev dev;
chopstx_mutex_t mtx;
chopstx_cond_t cnd;
uint32_t device_state : 3; /* USB device status */
uint32_t screen_mem_owner : 1; /* 0: driver, 1: app */
uint32_t mode : 8;
uint16_t *screen_ptr;
};
static struct vrsp vrsp0;
/*
* Locate VRSP structure from interface number or endpoint number.
* Currently, it always returns vrsp0, because we only have the one.
*/
static struct vrsp *
vrsp_get (int interface, uint8_t ep_num)
{
struct vrsp *v = &vrsp0;
(void)interface;
(void)ep_num;
return v;
}
static void vrsp_update_finish (struct vrsp *v, int value);
/* USB Device Descriptor */
static const uint8_t vrsp_device_desc[18] = {
18, /* bLength */
DEVICE_DESCRIPTOR, /* bDescriptorType */
0x10, 0x01, /* bcdUSB = 1.1 */
0x00, /* bDeviceClass. */
0x00, /* bDeviceSubClass. */
0x00, /* bDeviceProtocol. */
0x40, /* bMaxPacketSize. */
0xFF, 0xFF, /* idVendor */
0x01, 0x00, /* idProduct */
0x00, 0x01, /* bcdDevice */
1, /* iManufacturer. */
2, /* iProduct. */
3, /* iSerialNumber. */
1 /* bNumConfigurations. */
};
#define FEATURE_BUS_POWERED 0x80
/* Configuration Descriptor */
static const uint8_t vrsp_config_desc[32] = {
9,
CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */
/* Configuration Descriptor.*/
32, 0x00, /* wTotalLength. */
0x01, /* bNumInterfaces. */
0x01, /* bConfigurationValue. */
0, /* iConfiguration. */
FEATURE_BUS_POWERED, /* bmAttributes. */
50, /* bMaxPower (100mA). */
/* Interface Descriptor.*/
9,
INTERFACE_DESCRIPTOR,
0x00, /* bInterfaceNumber. */
0x00, /* bAlternateSetting. */
0x02, /* bNumEndpoints. */
0xFF, /* bInterfaceClass (Vendor specific). */
0x00, /* bInterfaceSubClass. */
0x00, /* bInterfaceProtocol. */
0, /* iInterface. */
/* Endpoint 1-OUT Descriptor.*/
7,
ENDPOINT_DESCRIPTOR,
ENDP1, /* bEndpointAddress. */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval. */
/* Endpoint 1-IN Descriptor.*/
7,
ENDPOINT_DESCRIPTOR,
ENDP1|0x80, /* bEndpointAddress. */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval. */
};
/*
* U.S. English language identifier.
*/
static const uint8_t vrsp_string0[4] = {
4, /* bLength */
STRING_DESCRIPTOR,
0x09, 0x04 /* LangID = 0x0409: US-English */
};
static const uint8_t vrsp_string1[] = {
23*2+2, /* bLength */
STRING_DESCRIPTOR, /* bDescriptorType */
/* Manufacturer: "Flying Stone Technology" */
'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0,
't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0,
'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0,
};
static const uint8_t vrsp_string2[] = {
14*2+2, /* bLength */
STRING_DESCRIPTOR, /* bDescriptorType */
/* Product name: "Chopstx Sample" */
'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0,
'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0,
};
/*
* Serial Number string.
*/
static const uint8_t vrsp_string3[10] = {
10, /* bLength */
STRING_DESCRIPTOR, /* bDescriptorType */
'0', 0, '.', 0, '0', 0, '0', 0, /* Version number */
};
#define NUM_INTERFACES 1
static void
usb_device_reset (struct usb_dev *dev)
{
usb_lld_reset (dev, FEATURE_BUS_POWERED);
chopstx_mutex_lock (&vrsp0.mtx);
vrsp0.device_state = USB_DEVICE_STATE_ATTACHED;
vrsp0.screen_mem_owner = 0;
vrsp0.screen_ptr = screen_image;
vrsp0.mode = 2;
chopstx_cond_signal (&vrsp0.cnd);
chopstx_mutex_unlock (&vrsp0.mtx);
}
static void
usb_ctrl_write_finish (struct usb_dev *dev)
{
(void)dev;
}
static int
usb_setup (struct usb_dev *dev)
{
struct device_req *arg = &dev->dev_req;
uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT);
if (USB_SETUP_SET (arg->type))
{
if (type_rcp == (VENDOR_REQUEST | INTERFACE_RECIPIENT)
&& arg->request == 0x00
&& arg->index == 0 /* interface 0 */)
{
struct vrsp *v = vrsp_get (arg->index, 0);
/* Finish the screen update. */
vrsp_update_finish (v, arg->value);
return usb_lld_ctrl_ack (dev);
}
}
return -1;
}
static int
usb_get_descriptor (struct usb_dev *dev)
{
struct device_req *arg = &dev->dev_req;
uint8_t rcp = arg->type & RECIPIENT;
uint8_t desc_type = (arg->value >> 8);
uint8_t desc_index = (arg->value & 0xff);
if (rcp != DEVICE_RECIPIENT)
return -1;
if (desc_type == DEVICE_DESCRIPTOR)
return usb_lld_ctrl_send (dev,
vrsp_device_desc, sizeof (vrsp_device_desc));
else if (desc_type == CONFIG_DESCRIPTOR)
return usb_lld_ctrl_send (dev,
vrsp_config_desc, sizeof (vrsp_config_desc));
else if (desc_type == STRING_DESCRIPTOR)
{
const uint8_t *str;
int size;
switch (desc_index)
{
case 0:
str = vrsp_string0;
size = sizeof (vrsp_string0);
break;
case 1:
str = vrsp_string1;
size = sizeof (vrsp_string1);
break;
case 2:
str = vrsp_string2;
size = sizeof (vrsp_string2);
break;
case 3:
str = vrsp_string3;
size = sizeof (vrsp_string3);
break;
default:
return -1;
}
return usb_lld_ctrl_send (dev, str, size);
}
return -1;
}
static const uint8_t status_zero;
static void
vrsp_setup_endpoints_for_interface (struct usb_dev *dev,
uint16_t interface, int stop)
{
if (interface == 0)
{
if (!stop)
{
struct vrsp *v = vrsp_get (interface, 0);
usb_lld_setup_endp (dev, ENDP1, EP_BULK, 1, 1);
if (v->screen_mem_owner == 0)
usb_lld_tx_enable_buf (&v->dev, ENDP1, &status_zero, 1);
}
else
{
usb_lld_stall_rx (dev, ENDP1);
usb_lld_stall_tx (dev, ENDP1);
}
}
}
static int
usb_set_configuration (struct usb_dev *dev)
{
uint8_t current_conf;
current_conf = usb_lld_current_configuration (dev);
if (current_conf == 0)
{
if (dev->dev_req.value != 1)
return -1;
usb_lld_set_configuration (dev, 1);
vrsp_setup_endpoints_for_interface (dev, 0, 0);
}
else if (current_conf != dev->dev_req.value)
{
if (dev->dev_req.value != 0)
return -1;
usb_lld_set_configuration (dev, 0);
vrsp_setup_endpoints_for_interface (dev, 0, 1);
}
return usb_lld_ctrl_ack (dev);
}
static int
usb_set_interface (struct usb_dev *dev)
{
uint16_t interface = dev->dev_req.index;
uint16_t alt = dev->dev_req.value;
if (interface >= NUM_INTERFACES)
return -1;
if (alt != 0)
return -1;
else
{
vrsp_setup_endpoints_for_interface (dev, interface, 0);
return usb_lld_ctrl_ack (dev);
}
}
static int
usb_get_interface (struct usb_dev *dev)
{
const uint8_t zero = 0;
uint16_t interface = dev->dev_req.index;
if (interface >= NUM_INTERFACES)
return -1;
/* We don't have alternate interface, so, always return 0. */
return usb_lld_ctrl_send (dev, &zero, 1);
}
static int
usb_get_status_interface (struct usb_dev *dev)
{
const uint16_t status_info = 0;
uint16_t interface = dev->dev_req.index;
if (interface >= NUM_INTERFACES)
return -1;
return usb_lld_ctrl_send (dev, &status_info, 2);
}
static void
vrsp_update_receive (struct vrsp *v, int len)
{
chopstx_mutex_lock (&v->mtx);
if (v->screen_mem_owner == 0 && v->screen_ptr)
{
v->screen_ptr += len / 2;
if (v->screen_ptr < screen_image + 12800)
usb_lld_rx_enable_buf (&v->dev, ENDP1, v->screen_ptr, 64);
}
chopstx_mutex_unlock (&v->mtx);
}
static void
vrsp_update_finish (struct vrsp *v, int value)
{
chopstx_mutex_lock (&v->mtx);
if (v->screen_mem_owner == 0 && v->screen_ptr)
{
v->screen_ptr = NULL;
v->mode = value;
chopstx_cond_signal (&v->cnd);
}
chopstx_mutex_unlock (&v->mtx);
}
static void
usb_tx_done (uint8_t ep_num, uint16_t len)
{
struct vrsp *v = vrsp_get (-1, ep_num);
(void)len;
/*
* Coming here means that notification ("it's ready to accept screen
* data") is retrieved by host. Enable the receive endpoint to
* accept data.
*/
/* EP_NUM should be ENDP1 in this implementation. */
usb_lld_rx_enable_buf (&v->dev, ENDP1, v->screen_ptr, 64);
}
static void
usb_rx_ready (uint8_t ep_num, uint16_t len)
{
struct vrsp *v = vrsp_get (-1, ep_num);
if (ep_num == ENDP1)
vrsp_update_receive (v, len);
}
#define PRIO_VRSP 4
#define STACK_PROCESS_2
#include "stack-def.h"
#define STACK_ADDR_VRSP ((uint32_t)process2_base)
#define STACK_SIZE_VRSP (sizeof process2_base)
static void *
vrsp_main (void *arg)
{
struct vrsp *v = arg;
struct usb_dev *dev = &v->dev;
int e;
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
usb_lld_init (dev, FEATURE_BUS_POWERED);
while (1)
{
chopstx_intr_wait (&usb_intr);
if (usb_intr.ready)
{
uint8_t ep_num;
/*
* When interrupt is detected, call usb_lld_event_handler.
* The event may be one of following:
* (1) Transfer to endpoint (bulk or interrupt)
* In this case EP_NUM is encoded in the variable E.
* (2) "NONE" event: some trasfer was done, but all was
* done by lower layer, no other work is needed in
* upper layer.
* (3) Device events: Reset or Suspend
* (4) Device requests to the endpoint zero.
*
*/
e = usb_lld_event_handler (dev);
chopstx_intr_done (&usb_intr);
ep_num = USB_EVENT_ENDP (e);
if (ep_num != 0)
{
if (USB_EVENT_TXRX (e))
usb_tx_done (ep_num, USB_EVENT_LEN (e));
else
usb_rx_ready (ep_num, USB_EVENT_LEN (e));
}
else
switch (USB_EVENT_ID (e))
{
case USB_EVENT_DEVICE_RESET:
usb_device_reset (dev);
continue;
case USB_EVENT_DEVICE_ADDRESSED:
/* The addres is assigned to the device. We don't
* need to do anything for this actually, but in this
* application, we maintain the USB status of the
* device. Usually, just "continue" as EVENT_OK is
* OK.
*/
chopstx_mutex_lock (&v->mtx);
v->device_state = USB_DEVICE_STATE_ADDRESSED;
chopstx_cond_signal (&v->cnd);
chopstx_mutex_unlock (&v->mtx);
continue;
case USB_EVENT_GET_DESCRIPTOR:
if (usb_get_descriptor (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_SET_CONFIGURATION:
if (usb_set_configuration (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_SET_INTERFACE:
if (usb_set_interface (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_CTRL_REQUEST:
/* Device specific device request. */
if (usb_setup (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_GET_STATUS_INTERFACE:
if (usb_get_status_interface (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_GET_INTERFACE:
if (usb_get_interface (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_SET_FEATURE_DEVICE:
case USB_EVENT_SET_FEATURE_ENDPOINT:
case USB_EVENT_CLEAR_FEATURE_DEVICE:
case USB_EVENT_CLEAR_FEATURE_ENDPOINT:
usb_lld_ctrl_ack (dev);
continue;
case USB_EVENT_CTRL_WRITE_FINISH:
/* Control WRITE transfer finished. */
usb_ctrl_write_finish (dev);
continue;
case USB_EVENT_OK:
case USB_EVENT_DEVICE_SUSPEND:
default:
continue;
}
}
}
return NULL;
}
static int
vrsp_ready_check (void *arg)
{
struct vrsp *v = arg;
return v->screen_mem_owner == 0 && v->screen_ptr == NULL;
}
/* API exposed to application. */
struct vrsp *
vrsp_open (void)
{
struct vrsp *v = &vrsp0; /* Now, we only have a single VRSP. */
chopstx_mutex_init (&v->mtx);
chopstx_cond_init (&v->cnd);
v->device_state = USB_DEVICE_STATE_UNCONNECTED;
v->screen_mem_owner = 0;
v->screen_ptr = screen_image;
v->mode = 1;
chopstx_create (PRIO_VRSP, STACK_ADDR_VRSP, STACK_SIZE_VRSP, vrsp_main, v);
return v;
}
void
vrsp_prepare_poll (struct vrsp *v, chopstx_poll_cond_t *poll_desc)
{
poll_desc->type = CHOPSTX_POLL_COND;
poll_desc->ready = 0;
poll_desc->cond = &v->cnd;
poll_desc->mutex = &v->mtx;
poll_desc->check = vrsp_ready_check;
poll_desc->arg = v;
}
/*
* vrsp_screen_acquire: Acquire the screen memory by the application to
* update the LCD.
*
* Returns < 0 on error
* >= 0 on success
*/
int
vrsp_screen_acquire (struct vrsp *v)
{
int r = 0;
chopstx_mutex_lock (&v->mtx);
while (1)
if (v->screen_mem_owner != 0)
{
/* something goes wrong, it's your turn already. */
r = -1;
break;
}
else if (v->screen_ptr)
/* The use of screen has not been finished yet. */
chopstx_cond_wait (&v->cnd, &v->mtx);
else
{
v->screen_mem_owner = 1;
r = v->mode;
break;
}
chopstx_mutex_unlock (&v->mtx);
return r;
}
/*
* vrsp_screen_release: release the screen memory by the application
*/
void
vrsp_screen_release (struct vrsp *v)
{
chopstx_mutex_lock (&v->mtx);
v->screen_mem_owner = 0;
v->screen_ptr = screen_image;
chopstx_mutex_unlock (&v->mtx);
/* Put the notification to host. */
usb_lld_tx_enable_buf (&v->dev, ENDP1, &status_zero, 1);
}

View File

@@ -1,7 +0,0 @@
struct vrsp;
struct vrsp *vrsp_open (void);
void vrsp_prepare_poll (struct vrsp *v, chopstx_poll_cond_t *poll_desc);
int vrsp_screen_acquire (struct vrsp *v);
void vrsp_screen_release (struct vrsp *v);

View File

@@ -15,7 +15,6 @@ LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld.m3
CSRC = sample.c
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes

View File

@@ -8,7 +8,6 @@ LDSCRIPT= lcd.ld
CSRC = primer2-switches.c primer2-ts.c lcd.c main.c \
neug.c sha256.c
ARCH=cortex-m
CHIP=stm32f103
USE_SYS = yes
USE_USB = yes

View File

@@ -15,7 +15,6 @@ LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld.m3
CSRC = sample.c
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes
USE_USART = yes

View File

@@ -6,7 +6,6 @@ CHOPSTX = ..
LDSCRIPT= sample.ld
CSRC = sample.c usb-cdc.c
ARCH=cortex-m
CHIP=stm32f103
USE_SYS = yes

View File

@@ -1,22 +0,0 @@
#include <stdint.h>
#include <mcu/gd32vf103.h>
asm (
".equ wfe,0x810\n\t" /* Not used (yet). */
".equ sleepvalue,0x811" /* Not used (yet). */
);
extern int chx_allow_sleep;
void
chx_sleep_mode (int how)
{
/*TBD*/
(void)how;
}
void
chx_prepare_sleep_mode (void)
{
/*TBD*/
}

View File

@@ -1,79 +0,0 @@
#include <stdint.h>
#include <mcu/gd32vf103.h>
static void __attribute__((used,section(".text.startup.1")))
clock_init (void)
{
/* HXTAL setup */
RCU->CTL |= RCU_CTL_HXTALEN;
while (!(RCU->CTL & RCU_CTL_HXTALSTB))
;
RCU->CFG0 &= ~RCU_CFG0_AHB_APB1_APB2_MASK;
/* CK_AHB = CK_SYS */
RCU->CFG0 |= RCU_CFG0_AHB_CKSYS_DIV1;
/* CK_APB2 = CK_AHB */
RCU->CFG0 |= RCU_CFG0_APB2_CKAHB_DIV1;
/* CK_APB1 = CK_AHB/2 */
RCU->CFG0 |= RCU_CFG0_APB1_CKAHB_DIV2;
/* CK_USBFS = 48MHz */
RCU->CFG0 |= RCU_CFG0_USBFSPSC_DIV2;
/* CK_ADC, CK_TIMER1xxx, CK_TIMER0, CK_I2S */
/* PLL setup */
RCU->CFG0 &= ~RCU_CFG0_PLLSRC_PLLMF_MASK;
RCU->CFG0 |= RCU_CFG0_PLL_MUL_VALUE | RCU_CFG0_PLLSRC_HXTAL;
RCU->CFG1 &= ~RCU_CFG1_PREDV0SEL_MASK;
RCU->CFG1 |= RCU_CFG1_PREDV0SEL_HXTAL;
RCU->CTL |= RCU_CTL_PLLEN;
while (!(RCU->CTL & RCU_CTL_PLLSTB))
;
/* Select PLL as system clock */
RCU->CFG0 &= ~RCU_CFG0_SCS_MASK;
RCU->CFG0 |= RCU_CFG0_CKSYSSRC_PLL;
/* Wait until PLL is selected as system clock */
while (!(RCU->CFG0 & RCU_CFG0_SCSS_PLL))
;
/* Stop IRC8M */
RCU->CTL &= ~RCU_CTL_IRC8MEN;
/* Flash setup: TBD */
}
static void __attribute__((used,section(".text.startup.1")))
gpio_init (void)
{
RCU->APB2EN |= RCU_APB2_GPIO;
RCU->APB2RST = RCU_APB2_GPIO;
RCU->APB2RST = 0;
#ifdef AFIO_MAPR_SOMETHING
AFIO->MAPR |= AFIO_MAPR_SOMETHING;
#endif
/* LED is mandatory. We configure it always. */
GPIO_LED->ODR = VAL_GPIO_LED_ODR;
GPIO_LED->CRH = VAL_GPIO_LED_CRH;
GPIO_LED->CRL = VAL_GPIO_LED_CRL;
/* If there is USB enabler pin and it's independent, we configure it. */
#if defined(GPIO_USB) && defined(VAL_GPIO_USB_ODR)
GPIO_USB->ODR = VAL_GPIO_USB_ODR;
GPIO_USB->CRH = VAL_GPIO_USB_CRH;
GPIO_USB->CRL = VAL_GPIO_USB_CRL;
#endif
#ifdef GPIO_OTHER
GPIO_OTHER->ODR = VAL_GPIO_OTHER_ODR;
GPIO_OTHER->CRH = VAL_GPIO_OTHER_CRH;
GPIO_OTHER->CRL = VAL_GPIO_OTHER_CRL;
#endif
}

View File

@@ -1,59 +0,0 @@
struct RCU {
volatile uint32_t CTL;
volatile uint32_t CFG0;
volatile uint32_t INT;
volatile uint32_t APB2RST;
volatile uint32_t APB1RST;
volatile uint32_t AHBEN;
volatile uint32_t APB2EN;
volatile uint32_t APB1EN;
volatile uint32_t BDCTL;
volatile uint32_t RSTSCK;
volatile uint32_t AHBRST;
volatile uint32_t CFG1;
uint32_t rsv;
volatile uint32_t DSV;
};
static struct RCU *const RCU = (struct RCU *)0x40021000;
#define RCU_CTL_HXTALEN 0x00010000
#define RCU_CTL_HXTALSTB 0x00020000
#define RCU_CTL_PLLSTB 0x02000000
#define RCU_CTL_PLLEN 0x01000000
#define RCU_CTL_IRC8MEN 0x00000001
#define RCU_CFG0_ADC_MASK 0x0000c000
#define RCU_CFG0_AHB_APB1_APB2_MASK 0x00003ff0
#define RCU_CFG0_AHB_CKSYS_DIV1 0x00000000
#define RCU_CFG0_APB2_CKAHB_DIV1 0x00000000
#define RCU_CFG0_APB1_CKAHB_DIV2 0x00000400
#define RCU_CFG0_PLLSRC_PLLMF_MASK 0x203d0000
#define RCU_CFG0_PLL_MUL12 0x00280000
#define RCU_CFG0_PLLSRC_HXTAL 0x00010000
#define RCU_CFG0_SCS_MASK 0x00000003
#define RCU_CFG0_SCSS_PLL 0x00000008
#define RCU_CFG0_CKSYSSRC_PLL 0x00000002
#define RCU_CFG0_USBFSPSC_DIV2 0x00c00000
#define RCU_CFG1_PREDV0SEL_MASK 0x00010000
#define RCU_CFG1_PREDV0SEL_HXTAL 0x00000000
#define RCU_APB2_GPIOA 0x00000004
#define RCU_APB2_GPIOB 0x00000008
#define RCU_APB2_GPIOC 0x00000010
#define RCU_AHB_USBFS 0x00001000
/* Semantics is exactly same as STM32F103. */
struct GPIO {
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
};
static struct GPIO *const GPIOA = (struct GPIO *)0x40010800;
static struct GPIO *const GPIOB = (struct GPIO *)0x40010C00;
static struct GPIO *const GPIOC = (struct GPIO *)0x40011000;

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* usb-usbip.c - USB Device Emulation (server side) by USBIP
*
* Copyright (C) 2017, 2018 g10 Code GmbH
* Copyright (C) 2017, 2018, 2021 g10 Code GmbH
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
@@ -22,10 +22,26 @@
*/
/*
FIXME:
RESET handling
USB Shutdown
Use reply structure of its own
* This driver is intended to emulate USB full-speed device, which
* maximum packet size is 64.
*
* "USBIP" is actually URB over network (instead of USB packet over
* network), and its (current) naive protocol may expose possible
* demarcation problem, which never occurs in real host-device; In
* real host-device relationship, it is host side, which does
* composition/decomposition of URB to/from packets. In an
* implmentation of USB device with USBIP, it needs to be device side,
* which does composition/decomposition of URB to/from packets.
*
* In this implementation of USB driver, URB_DATA_SIZE is defined as
* (65544+10), because (major) target device intended is CCID. In the
* CCID specification, you can find the value 65544+10.
*/
/*
* FIXME:
* RESET handling
* USB Shutdown
* Use reply structure of its own
*/
#include <pthread.h>
@@ -96,7 +112,7 @@ struct urb {
struct urb *next;
struct urb *prev;
uint16_t remain;
uint32_t remain;
char *data_p;
pthread_t tid;
@@ -236,7 +252,67 @@ attach_device (char busid[32], size_t *len_p)
return (const char *)&usbip_usb_device;
}
#define URB_DATA_SIZE 65535
/*
* The number 65544 comes from ISO 7816-4, which defines data format
* of smartcard; CLS INS P1 P2 occupies four octets. Then, Lc-octet
* to represent size of command data block, then command data block.
* Lastly, Le-octet to represent size of response data block to be
* returned.
*
* /----- for CLS INS P1 P2
* |
* | /-- for Lc
* | |
* | | /-- for data block
* | | |
* | | | /-- for Le
* | | | |
* v v v v
* 4 + 3 + 65535 + 2 = 65544
*
* The number 10 comes from the CCID protocol specification; It is the
* header size of CCID. Besides, we can find the number 65544 in the
* CCID protocol specification, too.
*
* And... we have "AND ONE MORE" three times.
*
* We accept that, as Buddha did.
*
* There are different interpretations for the historical fact and the
* idiom. Most likely, nowadays, here, third-time is considered a
* strikeout, perhaps, due to popularity of baseball.
*/
#define URB_DATA_SIZE (65544+10+1+1+1)
/*
* The reasons why there are "+1" three times.
*
* Wrong (1): +1, because of confusion of data size maximum.
* Wrong (2): +1, because of confusion of the size for Le.
* Wrong (3): +1, because of keeping applying old patch.
*
* Something like this may occur, unfortunately, in our world.
*
* We need to consider the real case of the max buffer size for
* libusb_bulk_transfer with CCID (let us call this "CASE_MAX").
*
* (1) Although the data size maximum (for all cases) is 65536, it
* only occurs when Le size is zero. In the particular case of
* CASE_MAX, it is actually 65535. When just applying the max value
* 65536, +1 occurs.
*
* (2) Although maximum size to represent Le is 3, it only occurs when
* Lc size is zero. In the particular case of CASE_MAX, it is
* actually 2. When just applying the max value 3, +1 occurs.
*
* (3) Fedora keeps a old patch for Ominikey 3121. The patch was
* written when the value of CMD_BUF_SIZE in libccid was small for
* short extended APDU exchange. In the patch, it uses the value 11
* for the header size (instead of 10) of TPDU, for its special
* support. Historically, CMD_BUF_SIZE in libccid was updated
* to bigger value to handle extended APDU exchange. When just applying
* the old patch using 11, +1 occurs.
*/
struct usbip_msg_cmd {
uint32_t devid;
@@ -299,12 +375,14 @@ static int write_data_transaction (struct usb_control *usbc_p,
static int read_data_transaction (struct usb_control *usbc_p,
int ep_num, char *buf, uint16_t count);
#define USB_MAX_PACKET_SIZE 64 /* For USB fullspeed device. */
static int
hc_handle_control_urb (struct urb *urb)
{
int r;
uint16_t count;
uint16_t remain = urb->len;
uint32_t remain = urb->len;
uint64_t l;
if ((debug & DEBUG_USB))
@@ -325,10 +403,10 @@ hc_handle_control_urb (struct urb *urb)
while (r == 0)
{
if (remain > 64)
count = 64;
if (remain > USB_MAX_PACKET_SIZE)
count = USB_MAX_PACKET_SIZE;
else
count = remain;
count = (uint16_t)remain;
read (usbc_ep0.eventfd, &l, sizeof (l));
r = control_write_data_transaction (urb->data_p, count);
@@ -337,7 +415,7 @@ hc_handle_control_urb (struct urb *urb)
urb->data_p += count;
remain -= count;
if (count < 64)
if (count < USB_MAX_PACKET_SIZE)
break;
}
if (r >= 0)
@@ -353,10 +431,10 @@ hc_handle_control_urb (struct urb *urb)
while (1)
{
if (remain > 64)
count = 64;
if (remain > USB_MAX_PACKET_SIZE)
count = USB_MAX_PACKET_SIZE;
else
count = remain;
count = (uint16_t)remain;
read (usbc_ep0.eventfd, &l, sizeof (l));
r = control_read_data_transaction (urb->data_p, count);
@@ -368,7 +446,7 @@ hc_handle_control_urb (struct urb *urb)
remain -= r;
urb->data_p += r;
if (r < 64)
if (r < USB_MAX_PACKET_SIZE)
break;
}
@@ -516,10 +594,10 @@ hc_handle_data_urb (struct usb_control *usbc_p)
if ((debug & DEBUG_USB))
puts ("hc_hdu 0");
if (urb->remain > 64)
count = 64;
if (urb->remain > USB_MAX_PACKET_SIZE)
count = USB_MAX_PACKET_SIZE;
else
count = urb->remain;
count = (uint16_t)urb->remain;
if (urb->dir == USBIP_DIR_OUT)
{ /* Output from host to device. */
@@ -533,7 +611,7 @@ hc_handle_data_urb (struct usb_control *usbc_p)
urb->data_p += count;
urb->remain -= count;
if (urb->remain == 0 || count < 64)
if (urb->remain == 0 || count < USB_MAX_PACKET_SIZE)
{
size_t len = urb->len - urb->remain;
@@ -565,7 +643,7 @@ hc_handle_data_urb (struct usb_control *usbc_p)
urb->remain -= r;
urb->data_p += r;
if (urb->remain == 0 || r < 64)
if (urb->remain == 0 || r < USB_MAX_PACKET_SIZE)
{
size_t len = urb->len - urb->remain;
@@ -596,7 +674,7 @@ issue_get_desc (void)
{
struct urb *urb;
urb = malloc (sizeof (struct urb) + 64);
urb = malloc (sizeof (struct urb) + USB_MAX_PACKET_SIZE);
urb->next = urb->prev = urb;
@@ -606,14 +684,14 @@ issue_get_desc (void)
urb->setup[3] = 1; /* Value H: desc_type */
urb->setup[4] = 0; /* Index */
urb->setup[5] = 0;
urb->setup[6] = 64; /* Length */
urb->setup[6] = USB_MAX_PACKET_SIZE; /* Length */
urb->setup[7] = 0;
urb->data_p = urb->data;
urb->seq = 0;
urb->devid = 0;
urb->dir = USBIP_DIR_IN;
urb->ep = 0;
urb->remain = urb->len = 64;
urb->remain = urb->len = USB_MAX_PACKET_SIZE;
hc_handle_control_urb (urb);
return urb;
}
@@ -729,7 +807,7 @@ usbip_handle_urb (uint32_t seq)
leave:
msg.cmd = htonl (REP_URB_SUBMIT);
msg.seq = htonl (urb->seq);
msg.seq = htonl (seq);
memset (&msg_rep, 0, sizeof (msg_rep));
msg_rep.status = htonl (r);
@@ -1080,6 +1158,11 @@ usbip_run_server (void *arg)
exit (1);
}
fputs ("USBIP thread started.\n", stdout);
fputs ("You can use this by attaching following commands:\n", stdout);
fputs (" # modprobe vhci_hcd\n", stdout);
fputs (" # usbip attach -r 127.0.0.1 -b 1-1\n", stdout);
pollfds[1].fd = shutdown_notify_fd;
pollfds[1].events = POLLIN;
pollfds[1].revents = 0;

View File

@@ -1,7 +1,7 @@
# Chopstx make rules.
ifeq ($(EMULATION),)
CSRC += $(CHOPSTX)/entry-$(ARCH).c
CSRC += $(CHOPSTX)/entry.c
else
CSRC += $(CHOPSTX)/entry-gnu-linux.c
endif
@@ -62,36 +62,22 @@ LLIBDIR = $(patsubst %,-L%,$(LIBDIR))
VPATH = $(sort $(dir $(CSRC)))
###
ifeq ($(EMULATION),)
ifeq ($(ARCH),riscv32)
# For now, just for my use of picolibc
INCDIR += /usr/local/picolibc/riscv64-unknown-elf/include
LIBDIR += /usr/local/picolibc/riscv64-unknown-elf/lib/rv32imac/ilp32
#
MCFLAGS = -march=rv32imac -mabi=ilp32
LDFLAGS = $(MCFLAGS) -nodefaultlibs -nostartfiles -lc -T$(LDSCRIPT) \
-Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch
else
MCFLAGS = -mcpu=$(MCU)
LDFLAGS = $(MCFLAGS) -nostartfiles -T$(LDSCRIPT) \
-Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--gc-sections
endif
else
MCFLAGS =
LDFLAGS =
DEFS += -D_GNU_SOURCE
endif
DEFS += -DARCH_HEADER='"chopstx-$(ARCH).h"' -DARCH_IMPL='"chopstx-$(ARCH).c"'
CFLAGS = $(MCFLAGS) $(OPT) $(CWARN) -Wa,-alms=$(BUILDDIR)/$(notdir $(<:.c=.lst)) $(DEFS)
LDFLAGS += $(LLIBDIR)
ifeq ($(EMULATION),)
ifeq ($(ARCH),riscv32)
else
CFLAGS += -mthumb -mno-thumb-interwork -DTHUMB
LDFLAGS += -mthumb -mno-thumb-interwork
endif
endif
CFLAGS += -MD -MP -MF .dep/$(@F).d

View File

@@ -38,19 +38,11 @@ enum DESCRIPTOR_TYPE
#define USB_SETUP_GET(req) ((req & REQUEST_DIR) != 0)
struct device_req {
union {
struct {
uint8_t type;
uint8_t request;
uint16_t value;
uint16_t index;
uint16_t len;
};
struct {
uint32_t w0;
uint32_t w1;
};
};
uint8_t type;
uint8_t request;
uint16_t value;
uint16_t index;
uint16_t len;
};
struct ctrl_data {
@@ -59,27 +51,12 @@ struct ctrl_data {
uint8_t require_zlp;
};
#ifdef MCU_GD32VF1
#define USB_REQUIRE_TXRX_INFO 1
#endif
#ifdef USB_REQUIRE_TXRX_INFO
struct epctl {
uint8_t *addr;
uint16_t len;
};
#endif
struct usb_dev {
uint8_t configuration;
uint8_t feature;
uint8_t state;
struct device_req dev_req;
struct ctrl_data ctrl_data;
#ifdef USB_REQUIRE_TXRX_INFO
struct epctl epctl_tx[3];
struct epctl epctl_rx[3];
#endif
};
enum {
@@ -175,22 +152,7 @@ void usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len);
void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en);
void usb_lld_stall_tx (int ep_num);
void usb_lld_stall_rx (int ep_num);
#elif defined(MCU_GD32VF1)
#define INTR_REQ_USB 86
#define INTR_REQ_USB_WAKEUP 61
void usb_lld_tx_enable_buf (struct usb_dev *dev, int ep_num, const void *buf, size_t len);
void usb_lld_rx_enable_buf (struct usb_dev *dev, int ep_num, void *buf, size_t len);
/* EP_TYPE[1:0] EndPoint TYPE */
#define EP_CONTROL 0x00
#define EP_ISOCHRONOUS 0x01
#define EP_BULK 0x02
#define EP_INTERRUPT 0x03
void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int ep_type, int rx_en, int tx_en);
void usb_lld_stall_tx (struct usb_dev *dev, int ep_num);
void usb_lld_stall_rx (struct usb_dev *dev, int ep_num);
#else /* STM32L4 or STM32F103/GD32F103 */
#else
#if defined(MCU_STM32L4)
#define INTR_REQ_USB 67
#else