summaryrefslogtreecommitdiff
path: root/class/vendor/display/usbd_display.c
blob: b9dfd7aff5fc94ae9c6b6e1b6ee9bb4a2ec03b75 (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
/*
 * Copyright (c) 2026, sakumisu
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "usbd_core.h"
#include "usbd_display.h"

struct usbd_display_priv {
    struct usb_mempool pool;
    struct usbd_endpoint out_ep;
    struct usbd_endpoint in_ep;
    struct usbd_display_frame *current_frame;
} g_usbd_display;

int usbd_display_frame_create(struct usbd_display_frame *frame, uint32_t count)
{
    return usb_mempool_create(&g_usbd_display.pool, frame, sizeof(struct usbd_display_frame), count);
}

struct usbd_display_frame *usbd_display_frame_alloc(void)
{
    return (struct usbd_display_frame *)usb_mempool_alloc(&g_usbd_display.pool);
}

int usbd_display_frame_free(struct usbd_display_frame *frame)
{
    return usb_mempool_free(&g_usbd_display.pool, (uintptr_t *)frame);
}

int usbd_display_frame_send(struct usbd_display_frame *frame)
{
    return usb_mempool_send(&g_usbd_display.pool, (uintptr_t *)frame);
}

int usbd_display_frame_recv(struct usbd_display_frame **frame, uint32_t timeout)
{
    return usb_mempool_recv(&g_usbd_display.pool, (uintptr_t **)frame, timeout);
}

uint8_t usb_dispay_dummy[512];
volatile uint32_t usb_display_buf_offset;
volatile bool usb_display_ignore_frame;

static void display_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
    switch (event) {
        case USBD_EVENT_RESET:
            break;
        case USBD_EVENT_CONFIGURED:
            usb_display_buf_offset = 0;
            usb_display_ignore_frame = true;
            g_usbd_display.current_frame = NULL;
            usb_mempool_reset(&g_usbd_display.pool);
            usbd_ep_start_read(busid, g_usbd_display.out_ep.ep_addr, usb_dispay_dummy, usbd_get_ep_mps(0, g_usbd_display.out_ep.ep_addr));
            break;
        default:
            break;
    }
}

void usbd_display_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
    if (usb_display_ignore_frame) {
        // alloc frame for next at the end of current frame
        if ((nbytes % usbd_get_ep_mps(0, g_usbd_display.out_ep.ep_addr)) || (nbytes == 0)) {
            if (g_usbd_display.current_frame == NULL) {
                g_usbd_display.current_frame = usbd_display_frame_alloc();
                if (g_usbd_display.current_frame) {
                    usb_display_ignore_frame = false;
                    usb_display_buf_offset = 0;

                    goto get_frame;
                } else {
                    goto drop_frame;
                }
            } else {
                usb_display_ignore_frame = false;
                usb_display_buf_offset = 0;

                goto get_frame;
            }
        } else {
            goto drop_frame;
        }
    } else {
        struct usbd_disp_frame_header *header = (struct usbd_disp_frame_header *)&g_usbd_display.current_frame->frame_buf[0];
        struct usbd_display_frame *frame;

        if (header->payload_total > g_usbd_display.current_frame->frame_bufsize) {
            USB_LOG_ERR("frame overflow, drop it\r\n");
            usb_display_ignore_frame = true;

            goto drop_frame;
        }

        usb_display_buf_offset += nbytes;

        if ((nbytes % usbd_get_ep_mps(0, g_usbd_display.out_ep.ep_addr)) || (nbytes == 0)) {
            frame = g_usbd_display.current_frame;
            g_usbd_display.current_frame = NULL;

            frame->frame_format = header->type;
            frame->frame_size = header->payload_total;
            usbd_display_frame_send(frame);

            g_usbd_display.current_frame = usbd_display_frame_alloc();
            if (g_usbd_display.current_frame) {
                usb_display_ignore_frame = false;
                usb_display_buf_offset = 0;

                goto get_frame;
            } else {
                usb_display_ignore_frame = true;

                goto drop_frame;
            }
        } else {
            goto get_frame;
        }
    }
    return;

drop_frame:
    // drop current frame
    usbd_ep_start_read(busid, g_usbd_display.out_ep.ep_addr, usb_dispay_dummy, usbd_get_ep_mps(0, g_usbd_display.out_ep.ep_addr));
    return;
get_frame:
    USB_ASSERT((usb_display_buf_offset + 16384) <= g_usbd_display.current_frame->frame_bufsize);
    usbd_ep_start_read(busid, g_usbd_display.out_ep.ep_addr, &g_usbd_display.current_frame->frame_buf[usb_display_buf_offset], 16384);
    return;
}

void usbd_display_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
}

struct usbd_interface *usbd_display_init_intf(struct usbd_interface *intf,
                                              const uint8_t out_ep,
                                              const uint8_t in_ep,
                                              struct usbd_display_frame *frame,
                                              uint32_t count)
{
    intf->class_interface_handler = NULL;
    intf->class_endpoint_handler = NULL;
    intf->vendor_handler = NULL;
    intf->notify_handler = display_notify_handler;

    g_usbd_display.out_ep.ep_addr = out_ep;
    g_usbd_display.out_ep.ep_cb = usbd_display_bulk_out;
    g_usbd_display.in_ep.ep_addr = in_ep;
    g_usbd_display.in_ep.ep_cb = usbd_display_bulk_in;
    usbd_add_endpoint(0, &g_usbd_display.out_ep);
    usbd_add_endpoint(0, &g_usbd_display.in_ep);

    for (uint32_t i = 0; i < count; i++) {
        USB_ASSERT_MSG((frame[i].frame_bufsize % 16384) == 0, "frame_bufsize must be the multiple of 16384");
    }

    usbd_display_frame_create(frame, count);
    return intf;
}

int usbd_display_dequeue(struct usbd_display_frame **frame, uint32_t timeout)
{
    return usbd_display_frame_recv(frame, timeout);
}

int usbd_display_enqueue(struct usbd_display_frame *frame)
{
    return usbd_display_frame_free(frame);
}