Root/atusb/fw/usb/atu2.c

Source at commit 244e4491319edccc4bdbb97bf03a4a15b3dc242b created 8 years 5 days ago.
By Werner Almesberger, atusb/fw/usb/: added support for bulk IN EP 1
1/*
2 * fw/usb/atu2.c - Chip-specific driver for Atmel ATxxxU2 USB chips
3 *
4 * Written 2008-2011 by Werner Almesberger
5 * Copyright 2008-2011 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13/*
14 * Known issues:
15 * - no suspend/resume
16 * - we don't call back after failed transmissions,
17 * - we don't reset the EP buffer after failed receptions
18 * - enumeration often encounters an error -71 (from which it recovers)
19 */
20
21#include <stdint.h>
22
23#define F_CPU 8000000UL
24#include <util/delay.h>
25
26#include <avr/io.h>
27#include <avr/interrupt.h>
28#include "usb.h"
29#include "../board.h"
30
31
32#ifndef NULL
33#define NULL 0
34#endif
35
36#if 1
37#define BUG_ON(cond) do { if (cond) panic(); } while (0)
38#else
39#define BUG_ON(cond)
40#endif
41
42
43#define NUM_EPS 2
44
45
46struct ep_descr eps[NUM_EPS];
47
48
49static uint16_t usb_read_word(void)
50{
51    uint8_t low;
52
53    low = UEDATX;
54    return low | UEDATX << 8;
55}
56
57
58static void enable_addr(void *user)
59{
60    while (!(UEINTX & (1 << TXINI)));
61    UDADDR |= 1 << ADDEN;
62}
63
64
65int set_addr(uint8_t addr)
66{
67    UDADDR = addr;
68    usb_send(&eps[0], NULL, 0, enable_addr, NULL);
69    return 1;
70}
71
72
73void usb_ep_change(struct ep_descr *ep)
74{
75    if (ep->state == EP_TX) {
76        UENUM = ep-eps;
77        UEIENX |= 1 << TXINE;
78    }
79}
80
81
82static int ep_setup(void)
83{
84    struct setup_request setup;
85
86    BUG_ON(UEBCLX < 8);
87
88    setup.bmRequestType = UEDATX;
89    setup.bRequest = UEDATX;
90    setup.wValue = usb_read_word();
91    setup.wIndex = usb_read_word();
92    setup.wLength = usb_read_word();
93
94    if (!handle_setup(&setup))
95        return 0;
96    if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE)
97        usb_send(&eps[0], NULL, 0, NULL, NULL);
98    return 1;
99}
100
101
102static int ep_rx(struct ep_descr *ep)
103{
104    uint8_t size;
105
106    size = UEBCLX;
107    if (size > ep->end-ep->buf)
108        return 0;
109    while (size--)
110        *ep->buf++ = UEDATX;
111    if (ep->buf == ep->end) {
112        ep->state = EP_IDLE;
113        if (ep->callback)
114            ep->callback(ep->user);
115// if (ep == &eps[0])
116            usb_send(ep, NULL, 0, NULL, NULL);
117    }
118    return 1;
119}
120
121
122static void ep_tx(struct ep_descr *ep)
123{
124    uint8_t size = ep->end-ep->buf;
125    uint8_t left;
126
127    if (size > ep->size)
128        size = ep->size;
129    for (left = size; left; left--)
130        UEDATX = *ep->buf++;
131    if (size == ep->size)
132        return;
133    ep->state = EP_IDLE;
134}
135
136
137static void handle_ep(int n)
138{
139    struct ep_descr *ep = eps+n;
140    uint8_t mask;
141
142    UENUM = n;
143    if (UEINTX & (1 << RXSTPI)) {
144        /* @@@ EP_RX. EP_TX: cancel */
145        ep->state = EP_IDLE;
146        if (!ep_setup())
147            goto stall;
148        UEINTX = ~(1 << RXSTPI);
149    }
150    if (UEINTX & (1 << RXOUTI)) {
151        /* @@ EP_TX: cancel */
152        if (ep->state != EP_RX)
153            goto stall;
154        if (!ep_rx(ep))
155            goto stall;
156        /* @@@ gcc 4.5.2 wants this cast */
157        UEINTX = (uint8_t) ~(1 << RXOUTI | 1 << FIFOCON);
158    }
159    if (UEINTX & (1 << STALLEDI)) {
160        ep->state = EP_IDLE;
161        UEINTX = ~(1 << STALLEDI);
162    }
163    if (UEINTX & (1 << TXINI)) {
164        /* @@ EP_RX: cancel (?) */
165        if (ep->state == EP_TX) {
166            ep_tx(ep);
167            mask = 1 << TXINI;
168            if (n)
169                mask |= 1 << FIFOCON;
170            UEINTX = ~mask;
171            if (ep->state == EP_IDLE && ep->callback)
172                ep->callback(ep->user);
173        } else {
174            UEIENX &= ~(1 << TXINE);
175        }
176    }
177    return;
178
179stall:
180    UEINTX = ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI);
181    ep->state = EP_IDLE;
182    UECONX |= 1 << STALLRQ;
183}
184
185
186static void ep_init(void)
187{
188    UENUM = 0;
189    UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
190    UECFG0X = 0; /* control, direction is ignored */
191    UECFG1X = 3 << EPSIZE0; /* 64 bytes */
192    UECFG1X |= 1 << ALLOC;
193
194    while (!(UESTA0X & (1 << CFGOK)));
195
196    UEIENX =
197        (1 << RXSTPE) | (1 << RXOUTE) | (1 << STALLEDE) | (1 << TXINE);
198
199    eps[0].state = EP_IDLE;
200    eps[0].size = 64;
201
202    UENUM = 1;
203    UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
204    UECFG0X = (1 << EPTYPE1) | (1 << EPDIR); /* bulk IN */
205    UECFG1X = 3 << EPSIZE0; /* 64 bytes */
206    UECFG1X |= 1 << ALLOC;
207
208    while (!(UESTA0X & (1 << CFGOK)));
209
210    UEIENX = (1 << STALLEDE) | (1 << TXINE);
211
212    eps[1].state = EP_IDLE;
213    eps[1].size = 64;
214}
215
216
217ISR(USB_GEN_vect)
218{
219    uint8_t flags;
220
221    flags = UDINT;
222    if (flags & (1 << EORSTI)) {
223        if (user_reset)
224            user_reset();
225        ep_init();
226        UDINT = ~(1 << EORSTI);
227    }
228}
229
230
231ISR(USB_COM_vect)
232{
233    uint8_t flags, i;
234
235    flags = UEINT;
236    for (i = 0; i != NUM_EPS; i++)
237        if (flags & (1 << i))
238            handle_ep(i);
239}
240
241
242void usb_reset(void)
243{
244    UDCON |= 1 << DETACH; /* detach the pull-up */
245    _delay_ms(1);
246}
247
248
249void usb_init(void)
250{
251    USBCON |= 1 << FRZCLK; /* freeze the clock */
252
253    /* enable the PLL and wait for it to lock */
254    PLLCSR &= ~(1 << PLLP2 | 1 << PLLP1 | 1 << PLLP0);
255    PLLCSR |= 1 << PLLE;
256    while (!(PLLCSR & (1 << PLOCK)));
257
258    USBCON &= ~(1 << USBE); /* reset the controller */
259    USBCON |= 1 << USBE;
260
261    USBCON &= ~(1 << FRZCLK); /* thaw the clock */
262
263    UDCON &= ~(1 << DETACH); /* attach the pull-up */
264    UDIEN = 1 << EORSTE; /* enable device interrupts */
265// UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
266
267    ep_init();
268}
269

Archive Download this file



interactive