- static struct vsfsm_state_t *
- vsfshell_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
- {
- struct vsfshell_t *shell = (struct vsfshell_t *)sm->user_data;
-
- switch (evt)
- {
- case VSFSM_EVT_INIT:
- shell->output_interrupted = false;
- shell->tbuffer.buffer.buffer = (uint8_t *)shell->cmd_buff;
- shell->tbuffer.buffer.size = sizeof(shell->cmd_buff);
- shell->tbuffer.position = 0;
- vsfsm_crit_init(&shell->output_crit, VSFSHELL_EVT_OUTPUT_CRIT_AVAIL);
-
- shell->stream_rx->callback_rx.param = shell;
- shell->stream_rx->callback_rx.on_in_int =
- vsfshell_streamrx_callback_on_in_int;
- shell->stream_rx->callback_rx.on_connect_tx =
- vsfshell_streamrx_callback_on_txconn;
- shell->stream_tx->callback_tx.param = shell;
- shell->stream_tx->callback_tx.on_out_int =
- vsfshell_streamtx_callback_on_out_int;
- shell->stream_tx->callback_tx.on_connect_rx =
- vsfshell_streamtx_callback_on_rxconn;
-
- vsfshell_register_handlers(shell, vsfshell_handlers);
-
- // shell->output_pt is only called by shell->input_pt
- shell->output_pt.thread = (vsfsm_pt_thread_t)vsfshell_output_thread;
- shell->output_pt.sm = sm;
- shell->output_pt.user_data = shell;
- // shell->input_pt is used to hanlde the events from stream_rx
- shell->input_pt.thread = vsfshell_input_thread;
- shell->input_pt.sm = sm;
- shell->input_pt.user_data = shell;
- shell->input_pt.state = 0;
- shell->input_pt.thread(&shell->input_pt, VSFSM_EVT_INIT);
- // default input sm is shell itself
- shell->input_sm = &shell->sm;
-
- stream_connect_rx(shell->stream_rx);
- stream_connect_tx(shell->stream_tx);
- break;
- case VSFSHELL_EVT_STREAMRX_ONCONN:
- break;
- case VSFSHELL_EVT_STREAMTX_ONCONN:
- // pass to shell->input_pt
- shell->input_pt.thread(&shell->input_pt, evt);
- break;
- case VSFSHELL_EVT_STREAMRX_ONIN:
- if (shell->input_sm == &shell->sm)
- {
- // pass to shell->input_sm
- shell->input_pt.thread(&shell->input_pt, evt);
- }
- else if (shell->input_sm != NULL)
- {
- vsfsm_post_evt(shell->input_sm, evt);
- }
- break;
- case VSFSHELL_EVT_STREAMTX_ONOUT:
- if (shell->input_sm == &shell->sm)
- {
- // pass to shell->input_sm
- shell->input_pt.thread(&shell->input_pt, evt);
- }
- else if (shell->output_sm != NULL)
- {
- vsfsm_post_evt(shell->output_sm, evt);
- }
- break;
- }
-
- return NULL;
- }
VSFSM_EVT_INIT一如既往的是初始化事件,初始化相关的各个子线程和资源。另外4个事件,就是流构架中的4个事件。
当然,首先收到的,应该是VSFSHELL_EVT_STREAMTX_ONCONN,连接事件,这里调用input_pt子线程来处理。结合代码如下:
- // vsfshell_input_thread is used to process the events
- // from the sender of the stream_rx
- vsf_err_t vsfshell_input_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
- {
- struct vsfshell_t *shell = (struct vsfshell_t *)pt->user_data;
- struct vsfsm_pt_t *output_pt = &shell->output_pt;
- char *cmd = (char *)shell->tbuffer.buffer.buffer;
- struct vsf_buffer_t buffer;
-
- vsfsm_pt_begin(pt);
- vsfsm_pt_wfe(pt, VSFSHELL_EVT_STREAMTX_ONCONN);
- vsfshell_printf(output_pt,
- "vsfshell 0.1 beta by SimonQian" VSFSHELL_LINEEND);
- vsfshell_printf(output_pt, VSFSHELL_PROMPT);
- while (1)
- {
- vsfsm_pt_wfe(pt, VSFSHELL_EVT_STREAMRX_ONIN);
- do
- {
- buffer.buffer = (uint8_t *)&shell->ch;
- buffer.size = 1;
- buffer.size = stream_rx(shell->stream_rx, &buffer);
- if (0 == buffer.size)
- {
- break;
- }
-
- if ('\r' == shell->ch)
- {
- vsfshell_printf(output_pt, VSFSHELL_LINEEND);
- }
- else if ('\b' == shell->ch)
- {
- if (shell->tbuffer.position)
- {
- vsfshell_printf(output_pt, "\b \b");
- shell->tbuffer.position--;
- }
- continue;
- }
- else if (!((shell->ch >= ' ') && (shell->ch <= '~')) ||
- (shell->tbuffer.position >= shell->tbuffer.buffer.size - 1))
- {
- continue;
- }
- else
- {
- vsfshell_printf(output_pt, "%c", shell->ch);
- }
-
- if ('\r' == shell->ch)
- {
- if (shell->tbuffer.position > 0)
- {
- // create new handler thread
- cmd[shell->tbuffer.position] = '\0';
- if (vsfshell_new_handler_thread(shell, cmd))
- {
- vsfshell_printf(output_pt,
- "Fail to execute : %s" VSFSHELL_LINEEND, cmd);
- vsfshell_printf(output_pt, VSFSHELL_PROMPT);
- }
- shell->tbuffer.position = 0;
- }
- break;
- }
- else
- {
- cmd[shell->tbuffer.position++] = shell->ch;
- }
- } while (buffer.size > 0);
- }
- vsfsm_pt_end(pt);
- return VSFERR_NOT_READY;
- }
input线程,首先调用vsfsm_pt_wfe来等待连接事件:
- vsfsm_pt_wfe(pt, VSFSHELL_EVT_STREAMTX_ONCONN);
之后,打印了命令行的名称和提示符后,就在while (1)中,等待VSFSHELL_EVT_STREAMRX_ONIN事件,也就是流接收到数据的事件。当然,这里貌似while (1)是阻塞的代码,实际还请看相关的PT线程的介绍。按键的事件处理代码很简单,无非就是处理退格、回车等特殊按键,把可现实的字符回发显示而已。如果按下回车后并且命令缓冲中有命令,就会调用vsfshell_new_handler_thread来动态的生成这个命令的处理函数,之后,流的输入和输出事件,就会有新的当前的命令处理线程来处理。直到命令处理线程调用vsfshell_handler_release_io释放对流的输入和输出的控制权。
对于输出流(用于显示字符)的处理,要稍微复杂一些。各个现成都可以输出字符给屏幕(包括释放了IO的后台运行的现成,用于打印一些信息),所以,各个输出之间的隔离保护就非常重要。每个线程都有自己的输出数据的子线程,这样,各个线程可以同时输出数据,不过只有得到控制权的现成能够输出,等控制权释放后,其他现成才能得到控制权。
线程中,输出数据使用vsfshell_printf,类似于printf。
- typedef vsf_err_t (*vsfshell_printf_thread_t)(struct vsfsm_pt_t *pt,
- vsfsm_evt_t evt, const char *format, ...);
- #define vsfshell_printf(output_pt, format, ...)\
- do {\
- (output_pt)->state = 0;\
- (output_pt)->sm = (pt)->sm;\
- vsfsm_pt_entry(pt);\
- {\
- vsfshell_printf_thread_t thread =\
- (vsfshell_printf_thread_t)(output_pt)->thread;\
- vsf_err_t __err = thread(output_pt, evt, format, ##__VA_ARGS__);\
- if (__err != VSFERR_NONE)\
- {\
- return __err;\
- }\
- }\
- } while (0)
看了这个代码就很明确了,vsfshell_printf其实只是初始化了output的子线程,并且调用换个现成,直到处理完成,这里可以对比一下vsfsm_pt_wfpt的定义:
- #define vsfsm_pt_wfpt(pt, ptslave) \
- do {\
- (ptslave)->state = 0;\
- (ptslave)->sm = (pt)->sm;\
- vsfsm_pt_entry(pt);\
- {\
- vsf_err_t __err = (ptslave)->thread(ptslave, evt);\
- if (__err != VSFERR_NONE)\
- {\
- return __err;\
- }\
- }\
- } while (0)
原来只是vsfsm_pt_wfpt的一个特例,因为printf的参数问题,所以使用这个方式来实现。
那么关键的就是,output现成的处理代码了,这里做了一些简化:
- // vsfshell_output_thread is used to process the events
- // from the receiver of the stream_tx
- vsf_err_t vsfshell_output_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
- const char *format, ...)
- {
- struct vsfshell_t *shell = (struct vsfshell_t *)pt->user_data;
- uint32_t str_len, size_avail;
- struct vsf_buffer_t buffer;
- va_list ap;
- char *printf_buff;
- uint32_t printf_size;
-
- vsfsm_pt_begin(pt);
- // get lock here
- if (vsfsm_crit_enter(pt->sm, &shell->output_crit))
- {
- vsfsm_pt_wfe(pt, VSFSHELL_EVT_OUTPUT_CRIT_AVAIL);
- }
- shell->output_sm = pt->sm;
- printf_size = sizeof(shell->printf_buff);
- printf_buff = shell->printf_buff;
- str_len = vsnprintf(printf_buff, printf_size, format, ap);
- va_end(ap);
- shell->printf_pos = shell->printf_buff;
-
- while (str_len > 0)
- {
- size_avail = stream_get_free_size(shell->stream_tx);
- if (!size_avail)
- {
- vsfsm_pt_wfe(pt, VSFSHELL_EVT_STREAMTX_ONOUT);
- size_avail = stream_get_free_size(shell->stream_tx);
- str_len = strlen(shell->printf_pos);
- }
-
- if (size_avail)
- {
- buffer.buffer = (uint8_t *)shell->printf_pos;
- buffer.size = min(str_len, size_avail);
- buffer.size = stream_tx(shell->stream_tx, &buffer);
- shell->printf_pos += buffer.size;
- str_len = strlen(shell->printf_pos);
- }
- }
- shell->output_sm = NULL;
- vsfsm_crit_leave(pt->sm, &shell->output_crit);
- vsfsm_pt_end(pt);
-
- return VSFERR_NONE;
- }
首先,通过一下代码申请输出的控制权,如果控制权不可用,就会等待VSFSHELL_EVT_OUTPUT_CRIT_AVAIL事件:
- if (vsfsm_crit_enter(pt->sm, &shell->output_crit))
- {
- vsfsm_pt_wfe(pt, VSFSHELL_EVT_OUTPUT_CRIT_AVAIL);
- }
- shell->output_sm = pt->sm;
得到控制权后,就把当前的输出现成设置为自己了,之后的事件也会发送给自己处理。 之后就是va_arg的标准处理代码。然后就是不停的把数据写入的发送的流中去,当然如果流中空余数据不够,就需要等待VSFSHELL_EVT_STREAMTX_ONOUT事件,等流的另外一段把数据读取出来,使得可以再次写入数据到流中去。最后,就是释放输出的控制权,如果其他线程也要输出的话,就会得到一个VSFSHELL_EVT_OUTPUT_CRIT_AVAIL事件。这样,多线程同时输出的话,数据并不会打架,而是先到先得,依次输出。