Root/atusb/fw/usb/atu2.c

Source at commit d233c04c86c2fde39b5e069bb6750caef3393f32 created 8 years 8 months ago.
By Werner Almesberger, atusb/fw: implemented USB bus reset (to host) and polling of reset from host
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 "usb.h"
28#include "../board.h"
29
30
31#ifndef NULL
32#define NULL 0
33#endif
34
35#if 1
36#define BUG_ON(cond) do { if (cond) panic(); } while (0)
37#else
38#define BUG_ON(cond)
39#endif
40
41
42#define NUM_EPS 1
43
44
45struct ep_descr eps[NUM_EPS];
46
47
48static uint16_t usb_read_word(void)
49{
50    uint8_t low;
51
52    low = UEDATX;
53    return low | UEDATX << 8;
54}
55
56
57static void enable_addr(void *user)
58{
59    while (!(UEINTX & (1 << TXINI)));
60    UDADDR |= 1 << ADDEN;
61}
62
63
64int set_addr(uint8_t addr)
65{
66    UDADDR = addr;
67    usb_send(&eps[0], NULL, 0, enable_addr, NULL);
68    return 1;
69}
70
71
72static int ep_setup(void)
73{
74    struct setup_request setup;
75
76    BUG_ON(UEBCLX < 8);
77
78    setup.bmRequestType = UEDATX;
79    setup.bRequest = UEDATX;
80    setup.wValue = usb_read_word();
81    setup.wIndex = usb_read_word();
82    setup.wLength = usb_read_word();
83
84    if (!handle_setup(&setup))
85        return 0;
86    if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE)
87        usb_send(&eps[0], NULL, 0, NULL, NULL);
88    return 1;
89}
90
91
92static int ep_rx(struct ep_descr *ep)
93{
94    uint8_t size;
95
96    size = UEBCLX;
97    if (size > ep->end-ep->buf)
98        return 0;
99    while (size--)
100        *ep->buf++ = UEDATX;
101    if (ep->buf == ep->end) {
102        ep->state = EP_IDLE;
103        if (ep->callback)
104            ep->callback(ep->user);
105        if (ep == &eps[0])
106            usb_send(ep, NULL, 0, NULL, NULL);
107    }
108    return 1;
109}
110
111
112static void ep_tx(struct ep_descr *ep)
113{
114    uint8_t size = ep->end-ep->buf;
115    uint8_t left;
116
117    if (size > ep->size)
118        size = ep->size;
119    for (left = size; left; left--)
120        UEDATX = *ep->buf++;
121    if (size == ep->size)
122        return;
123    ep->state = EP_IDLE;
124}
125
126
127static void handle_ep(int n)
128{
129    struct ep_descr *ep = eps+n;
130
131    UENUM = n;
132    if (UEINTX & (1 << RXSTPI)) {
133        /* @@@ EP_RX. EP_TX: cancel */
134        ep->state = EP_IDLE;
135        if (!ep_setup())
136            goto stall;
137        UEINTX &= ~(1 << RXSTPI);
138    }
139    if (UEINTX & (1 << RXOUTI)) {
140        /* @@ EP_TX: cancel */
141        if (ep->state != EP_RX)
142            goto stall;
143        if (!ep_rx(ep))
144            goto stall;
145// UEINTX &= ~(1 << RXOUTI);
146        UEINTX &= ~(1 << RXOUTI | 1 << FIFOCON);
147    }
148    if (UEINTX & (1 << STALLEDI)) {
149        ep->state = EP_IDLE;
150        UEINTX &= ~(1 << STALLEDI);
151    }
152    if (UEINTX & (1 << TXINI)) {
153        /* @@ EP_RX: cancel (?) */
154        if (ep->state == EP_TX) {
155            ep_tx(ep);
156            UEINTX &= ~(1 << TXINI);
157            if (ep->state == EP_IDLE && ep->callback)
158                ep->callback(ep->user);
159        }
160    }
161    return;
162
163stall:
164    UEINTX &= ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI);
165    ep->state = EP_IDLE;
166    UECONX |= 1 << STALLRQ;
167}
168
169
170void usb_poll(void)
171{
172    uint8_t flags, i;
173
174    flags = UDINT;
175    if (flags & EORSTI) {
176        UDINT &= ~(1 << EORSTI);
177        if (user_reset)
178            user_reset();
179    }
180    flags = UEINT;
181    for (i = 0; i != NUM_EPS; i++)
182        if (1 || flags & (1 << i))
183            handle_ep(i);
184    /* @@@ USB bus reset */
185}
186
187
188static void ep_init(void)
189{
190    UENUM = 0;
191    UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
192    UECFG0X = 0; /* control, direction is ignored */
193    UECFG1X = 3 << EPSIZE0; /* 64 bytes */
194    UECFG1X |= 1 << ALLOC;
195
196    while (!(UESTA0X & (1 << CFGOK)));
197
198    eps[0].state = EP_IDLE;
199    eps[0].size = 64;
200}
201
202
203void usb_reset(void)
204{
205    UDCON |= 1 << DETACH; /* detach the pull-up */
206    _delay_ms(1);
207}
208
209
210void usb_init(void)
211{
212    USBCON |= 1 << FRZCLK; /* freeze the clock */
213
214    /* enable the PLL and wait for it to lock */
215    PLLCSR &= ~(1 << PLLP2 | 1 << PLLP1 | 1 << PLLP0);
216    PLLCSR |= 1 << PLLE;
217    while (!(PLLCSR & (1 << PLOCK)));
218
219    USBCON &= ~(1 << USBE); /* reset the controller */
220    USBCON |= 1 << USBE;
221
222    USBCON &= ~(1 << FRZCLK); /* thaw the clock */
223
224    UDCON &= ~(1 << DETACH); /* attach the pull-up */
225    UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
226
227    ep_init();
228}
229

Archive Download this file



interactive