summaryrefslogtreecommitdiff
path: root/components/drivers/serial/bypass.c
blob: 9da1833b701a74cb9f399f30f40852c68f896212 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
 * Copyright (c) 2006-2024 RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-11-20     zhujiale     the first version
 */

#include<rtdevice.h>
#define DBG_TAG    "UART"
#define DBG_LVL    DBG_INFO
#include <rtdbg.h>

static struct rt_serial_bypass_func* rt_bypass_alloc_func(const char* name, rt_uint8_t level, bypass_function_t func, void* data)
{
    struct rt_serial_bypass_func* bypass;

    if (!func)
        return RT_NULL;

    bypass = rt_malloc(sizeof(struct rt_serial_bypass_func));
    rt_memset(bypass, 0, sizeof(struct rt_serial_bypass_func));

    if (rt_strlen(name) > RT_NAME_MAX - 1)
        rt_memcpy(bypass->name, name, RT_NAME_MAX);
    else
        rt_memcpy(bypass->name, name, rt_strlen(name) + 1);

    bypass->level = level;

    rt_list_init(&bypass->node);
    bypass->bypass = func;
    bypass->data = data;

    return bypass;
}

rt_err_t rt_serial_bypass_init(struct rt_serial_device* serial)
{
    serial->bypass = rt_malloc(sizeof(struct rt_serial_bypass));
    rt_memset(serial->bypass, 0, sizeof(struct rt_serial_bypass));
#ifdef RT_USING_SERIAL_V2
    serial->bypass->pipe = rt_ringbuffer_create(serial->config.rx_bufsz);
#else
    serial->bypass->pipe = rt_ringbuffer_create(serial->config.bufsz);
#endif
    serial->bypass->mutex = rt_mutex_create("serial_bypass", RT_IPC_FLAG_FIFO);

    return RT_EOK;
}

static rt_err_t rt_bypass_register(struct rt_serial_bypass_head* bypass, const char* name, rt_uint8_t level, bypass_function_t func, void* data)
{
    struct rt_serial_bypass_func* pass = RT_NULL;
    struct rt_list_node* node;
    int flags;

    RT_DEBUG_NOT_IN_INTERRUPT;

    pass = rt_bypass_alloc_func(name, level, func, data);

    RT_ASSERT(bypass != RT_NULL);

    node = bypass->head.next;

    if (node == &bypass->head)
    {
        rt_list_insert_before(&pass->node, node);
        return RT_EOK;
    }
    flags = rt_spin_lock_irqsave(&(bypass->spinlock));
    do {
        struct rt_serial_bypass_func* temp_curr;
        temp_curr = rt_container_of(node, struct rt_serial_bypass_func, node);

        if (level < temp_curr->level)
        {
            rt_list_insert_before(node, &pass->node);

            rt_spin_unlock_irqrestore(&(bypass->spinlock), flags);
            return RT_EOK;
        }
        else if (level == temp_curr->level)
        {
            rt_spin_unlock_irqrestore(&(bypass->spinlock), flags);
            LOG_E("Conflict: bypass [%s] level conflicts with [%s] at level [%d]\n", name, temp_curr->name, level);
            rt_free(pass);
            return -RT_ERROR;
        }

        node = node->next;

    } while (node != &bypass->head);

    rt_list_insert_before(&bypass->head, &pass->node);

    rt_spin_unlock_irqrestore(&(bypass->spinlock), flags);
    return RT_EOK;

}

rt_err_t rt_bypass_upper_register(struct rt_serial_device* serial, const char* name, rt_uint8_t level, bypass_function_t func, void* data)
{
    if (!serial->bypass)
        rt_serial_bypass_init(serial);

    if (!serial->bypass->upper_h)
    {
        serial->bypass->upper_h = rt_malloc(sizeof(struct rt_serial_bypass_head));
        rt_spin_lock_init(&serial->bypass->upper_h->spinlock);
        rt_list_init(&serial->bypass->upper_h->head);

    }

    return rt_bypass_register(serial->bypass->upper_h, name, level, func, data);
}



void rt_bypass_putchar(struct rt_serial_device* serial, rt_uint8_t ch)
{
    rt_mutex_take(serial->bypass->mutex, RT_WAITING_FOREVER);
    rt_ringbuffer_putchar(serial->bypass->pipe, ch);
    rt_mutex_release(serial->bypass->mutex);
}

rt_size_t rt_bypass_getchar(struct rt_serial_device* serial, rt_uint8_t* ch)
{
    int flags;
    rt_mutex_take(serial->bypass->mutex, RT_WAITING_FOREVER);
    flags = rt_ringbuffer_getchar(serial->bypass->pipe, ch);
    rt_mutex_release(serial->bypass->mutex);
    return flags;
}

static inline rt_err_t _bypass_getchar_form_serial_fifo(struct rt_serial_device* serial, char* ch)
{
    rt_base_t level;
    struct rt_serial_rx_fifo* rx_fifo;
    rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
    /* disable interrupt */
    level = rt_spin_lock_irqsave(&(serial->spinlock));

    /* there's no data: */
#ifdef RT_USING_SERIAL_V2
    rt_size_t  ringbuf_date_stat;
    ringbuf_date_stat = rt_ringbuffer_data_len(&rx_fifo->rb);
    if(!ringbuf_date_stat)
    {
        /* no data, enable interrupt and break out */
        rt_spin_unlock_irqrestore(&(serial->spinlock), level);
        return -RT_EEMPTY;
    }
    if(!rt_ringbuffer_getchar(&rx_fifo->rb, (rt_uint8_t *)ch))
    {
        /* Failed to read data */
        rt_spin_unlock_irqrestore(&(serial->spinlock), level);
        return -RT_EOK;
    }
#else
    if ((rx_fifo->get_index == rx_fifo->put_index) && (rx_fifo->is_full == RT_FALSE))
    {
        /* no data, enable interrupt and break out */
        rt_spin_unlock_irqrestore(&(serial->spinlock), level);
        return -RT_EEMPTY;
    }

    /* otherwise there's the data: */
    *ch = rx_fifo->buffer[rx_fifo->get_index];
    rx_fifo->get_index += 1;
    if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0;

    if (rx_fifo->is_full == RT_TRUE)
    {
        rx_fifo->is_full = RT_FALSE;
    }
#endif
    /* enable interrupt */
    rt_spin_unlock_irqrestore(&(serial->spinlock), level);

    return RT_EOK;
}

static void _lower_work(struct rt_serial_device* serial)
{
    struct rt_list_node* node;
    struct rt_serial_bypass_func* temp_curr = RT_NULL;
    if (serial->bypass && serial->bypass->lower_h)
    {
        while (1)
        {
            char ch;
            if (_bypass_getchar_form_serial_fifo(serial, &ch))
                return;

            node = serial->bypass->lower_h->head.next;

            while (node != &serial->bypass->lower_h->head)
            {
                temp_curr = rt_container_of(node, struct rt_serial_bypass_func, node);

                if (!temp_curr->bypass(serial, ch, temp_curr->data))
                {
                    break;
                }

                node = node->next;
            }
            if (node == &serial->bypass->lower_h->head)
            {
                rt_bypass_putchar(serial, ch);
            }
        }
    }
}


static void rt_lower_work(struct rt_work* work, void* work_data)
{
    struct rt_serial_device* serial = (struct rt_serial_device*)work_data;

    RT_ASSERT(serial != RT_NULL);

    _lower_work(serial);
}

rt_err_t rt_bypass_lower_register(struct rt_serial_device* serial, const char* name, rt_uint8_t level, bypass_function_t func, void* data)
{
    if (!serial->bypass)
        rt_serial_bypass_init(serial);

    if (!serial->bypass->lower_h)
    {
        serial->bypass->lower_workq = rt_workqueue_create("serial bypass", RT_SYSTEM_WORKQUEUE_STACKSIZE,
            RT_SYSTEM_WORKQUEUE_PRIORITY);
        rt_work_init(&serial->bypass->work, rt_lower_work, (void*)serial);

        serial->bypass->lower_h = rt_malloc(sizeof(struct rt_serial_bypass_head));
        rt_spin_lock_init(&serial->bypass->lower_h->spinlock);
        rt_list_init(&serial->bypass->lower_h->head);

    }

    return rt_bypass_register(serial->bypass->lower_h, name, level, func, data);
}

void rt_bypass_work_straight(struct rt_serial_device* serial)
{
    if (serial->bypass && serial->bypass->lower_h)
    {
        _lower_work(serial);
        return;
    }

    while (1)
    {
        char ch;
        if (_bypass_getchar_form_serial_fifo(serial, &ch))
            return;

        rt_bypass_putchar(serial, ch);
    }

}

rt_err_t rt_bypass_unregister(struct rt_serial_bypass_head* bypass, rt_uint8_t level)
{
    struct rt_list_node* node;
    struct rt_serial_bypass_func* temp_curr = RT_NULL;
    int flags;

    /*Can not unregister protect level in bypass it general be msh or tty*/
    if (level > RT_BYPASS_PROTECT_LEVEL_1)
        return -RT_ERROR;

    if (!bypass)
        return -RT_ERROR;

    node = bypass->head.next;
    flags = rt_spin_lock_irqsave(&(bypass->spinlock));

    do {
        temp_curr = rt_container_of(node, struct rt_serial_bypass_func, node);

        if (level == temp_curr->level)
        {
            rt_list_remove(node);
            rt_spin_unlock_irqrestore(&(bypass->spinlock), flags);
            rt_free(temp_curr);
            return RT_EOK;
        }

        node = node->next;
    } while (node != &bypass->head);

    LOG_E("Can't find bypass with level [%d]", level);
    rt_spin_unlock_irqrestore(&(bypass->spinlock), flags);
    return -RT_ERROR;
}

rt_err_t rt_bypass_upper_unregister(struct rt_serial_device* serial, rt_uint8_t level)
{
    if (!serial->bypass || !serial->bypass->upper_h)
        return -RT_ERROR;
    return rt_bypass_unregister(serial->bypass->upper_h, level);
}

rt_err_t rt_bypass_lower_unregister(struct rt_serial_device* serial, rt_uint8_t level)
{
    if (!serial->bypass || !serial->bypass->lower_h)
        return -RT_ERROR;
    return rt_bypass_unregister(serial->bypass->lower_h, level);
}

int serial_bypass_list(int argc, char** argv)
{
    struct rt_serial_device* serial = RT_NULL;
    struct rt_serial_bypass_func* current;
    struct rt_list_node* node;
    int flags;

    serial = (struct rt_serial_device*)rt_console_get_device();
    if (!serial || !serial->bypass)
    {
        rt_kprintf("Serial bypass not initialized.\n");
        return -1;
    }

    /* 遍历 Upper Bypass 链表 */
    if (serial->bypass->upper_h)
    {
        rt_kprintf("Upper bypass chain:\n");
        node = serial->bypass->upper_h->head.next;

        flags = rt_spin_lock_irqsave(&(serial->bypass->upper_h->spinlock)); /* 加锁*/
        while (node != &serial->bypass->upper_h->head)
        {
            current = rt_container_of(node, struct rt_serial_bypass_func, node);
            rt_kprintf(" - Name: [%s], Level: [%d]\n", current->name, current->level);
            node = node->next;
        }
        rt_spin_unlock_irqrestore(&(serial->bypass->upper_h->spinlock), flags); /* 解锁*/
    }
    else
    {
        rt_kprintf("Upper bypass chain is empty.\n");
    }

    /* 遍历 Lower Bypass 链表 */
    if (serial->bypass->lower_h)
    {
        rt_kprintf("Lower bypass chain:\n");
        node = serial->bypass->lower_h->head.next;

        flags = rt_spin_lock_irqsave(&(serial->bypass->lower_h->spinlock)); /* 加锁*/
        while (node != &serial->bypass->lower_h->head)
        {
            current = rt_container_of(node, struct rt_serial_bypass_func, node);
            rt_kprintf(" - Name: [%s], Level: [%d]\n", current->name, current->level);
            node = node->next;
        }
        rt_spin_unlock_irqrestore(&(serial->bypass->lower_h->spinlock), flags); /* 解锁*/
    }
    else
    {
        rt_kprintf("Lower bypass chain is empty.\n");
    }

    return 0;
}


MSH_CMD_EXPORT(serial_bypass_list, serial bypass list)