aboutsummaryrefslogtreecommitdiff
path: root/kernel/io/mcu/atmega2560/usart.c
blob: 6f5978745b52e651d89a52bf80254c4bc64b8641 (plain) (blame)
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
#include "io/io.h"
#include "collection/list.h"
#include "collection/rbuffer.h"
#include "task/sched.h"
#include "mcu/io/usart.h"
#include "mcu/task/context.h"
#include <avr/interrupt.h>
#include <avr/io.h>

struct usart_private {
    char __rx_buffer[USART_BUFFER_SIZE];
    char __tx_buffer[USART_BUFFER_SIZE];

    struct rbuffer_t rx_buffer;
    struct rbuffer_t tx_buffer;

    struct list_head rx_queue;
    struct list_head tx_queue;

    volatile unsigned char* const ucsrxa;
    volatile unsigned char* const ubrrxh;
    volatile unsigned char* const ubrrxl;
    volatile unsigned char* const ucsrxb;
    char u2xx;
    char rxenx;
    char txenx;
    char rxciex;
    char udriex;
};

int usart_open(struct file* usart) {
    struct usart_private* priv = (struct usart_private*) usart->private_data;

    INIT_LIST_HEAD(&priv->rx_queue);
    INIT_LIST_HEAD(&priv->tx_queue);
    INIT_RBUFFER(&priv->rx_buffer, priv->__rx_buffer, USART_BUFFER_SIZE);
    INIT_RBUFFER(&priv->tx_buffer, priv->__tx_buffer, USART_BUFFER_SIZE);

    return 0;
}

int usart_ioctl(struct file* usart, int cmd, long args) {
    struct usart_private* priv = (struct usart_private*) usart->private_data;

    switch (cmd) {
        case IOCTL_SET_BAUD: {
            long baud = args;
            (*priv->ucsrxa) |= (1 << priv->u2xx);
            long baud_setting = (F_CPU / 4 / baud - 1) / 2;
            (*priv->ubrrxh) = baud_setting >> 8;
            (*priv->ubrrxl) = baud_setting;
            (*priv->ucsrxb) |= (1 << priv->rxenx) | (1 << priv->txenx) | (1 << priv->rxciex);
            (*priv->ucsrxb) &= ~(1 << priv->udriex);
            return 0;
        }
        default:
            return -1;
    }
}

ssize_t usart_write(struct file* usart, const char* const buffer, size_t length) {
    struct usart_private* priv = (struct usart_private*) usart->private_data;
    volatile unsigned char* ucsrxb = (priv->ucsrxb);
    char write_enable = (1 << priv->udriex);

    ssize_t wrote = 0;
    int r = 0;
    do {
        cli();
        r = rbuffer_write(&priv->tx_buffer, buffer[wrote]);
        wrote += 1;
        sei();
        *ucsrxb |= write_enable;
    } while (r == 0 && wrote < length);
    return wrote;
}

ssize_t usart_read(struct file* usart, char* const buffer, size_t length) {
    struct usart_private* priv = (struct usart_private*) usart->private_data;
    while (rbuffer_empty(&priv->rx_buffer)) {
        sleep_queue(&priv->rx_queue);
        yield();
    }
    size_t read = 0;
    size_t r = 0;
    while (!rbuffer_empty(&priv->rx_buffer) && read < length && r == 0) {
        cli();
        r = rbuffer_read(&priv->rx_buffer, buffer + read);
        read += 1;
        sei();
    }
    return read;
}

int usart_close(struct file* usart) {
    return 0;
}

struct file_operations usart_fops = {
    .open = usart_open,
    .ioctl = usart_ioctl,
    .read = usart_read,
    .write = usart_write,
    .close = usart_close
};

struct usart_private _usart0 = {
    .ucsrxa = &UCSR0A,
    .ubrrxh = &UBRR0H,
    .ubrrxl = &UBRR0L,
    .ucsrxb = &UCSR0B,
    .u2xx = U2X0,
    .rxenx = RXEN0,
    .txenx = TXEN0,
    .rxciex = RXCIE0,
    .udriex = UDRIE0
};

struct file usart0 = {
    .fops = &usart_fops,
    .private_data = &_usart0
};

//called when data register is empty
ISR(USART0_UDRE_vect) {
    struct usart_private* priv = (struct usart_private*) usart0.private_data;
    char c;

    if (rbuffer_read(&priv->tx_buffer, &c) == 0) {
        UDR0 = c;
    } else {
        UCSR0B &= ~(1 << UDRIE0); //buffer empty, disable interruot
    }
}

//called when byte is received
ISR(USART0_RX_vect) {
    //context_save();

    struct usart_private* priv = (struct usart_private*) usart0.private_data;
    
    char c = UDR0;
    rbuffer_write(&priv->rx_buffer, c);
    wake_all_queue(&priv->rx_queue);

    //context_restore();
    //asm volatile ("reti");
}