atusb: Delete temporary file
[linux-libre-firmware.git] / atusb / usb / atu2.c
1 /*
2  * fw/usb/atu2.c - Chip-specific driver for Atmel ATxxxU2 USB chips
3  *
4  * Written 2008-2011, 2013-2014 by Werner Almesberger
5  * Copyright 2008-2011, 2013-2014 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 <stdbool.h>
22 #include <stdint.h>
23
24 #define F_CPU   8000000UL
25 #include <util/delay.h>
26
27 #include <avr/io.h>
28 #include <avr/interrupt.h>
29 #include "usb.h"
30 #include "board.h"
31
32
33 #ifndef NULL
34 #define NULL 0
35 #endif
36
37 #if 1
38 #define BUG_ON(cond)    do { if (cond) panic(); } while (0)
39 #else
40 #define BUG_ON(cond)
41 #endif
42
43
44 struct ep_descr eps[NUM_EPS];
45
46
47 static uint16_t usb_read_word(void)
48 {
49         uint8_t low;
50
51         low = UEDATX;
52         return low | UEDATX << 8;
53 }
54
55
56 static void enable_addr(void *user)
57 {
58         while (!(UEINTX & (1 << TXINI)));
59         UDADDR |= 1 << ADDEN;
60 }
61
62
63 void set_addr(uint8_t addr)
64 {
65         UDADDR = addr;
66         usb_send(&eps[0], NULL, 0, enable_addr, NULL);
67 }
68
69
70 void usb_ep_change(struct ep_descr *ep)
71 {
72         if (ep->state == EP_TX) {
73                 UENUM = ep-eps;
74                 UEIENX |= 1 << TXINE;
75         }
76 }
77
78
79 static bool ep_setup(void)
80 {
81         struct setup_request setup;
82
83         BUG_ON(UEBCLX < 8);
84
85         setup.bmRequestType = UEDATX;
86         setup.bRequest = UEDATX;
87         setup.wValue = usb_read_word();
88         setup.wIndex = usb_read_word();
89         setup.wLength = usb_read_word();
90
91         if (!handle_setup(&setup))
92                 return 0;
93         if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE)
94                 usb_send(&eps[0], NULL, 0, NULL, NULL);
95         return 1;
96 }
97
98
99 static bool ep_rx(struct ep_descr *ep)
100 {
101         uint8_t size;
102
103         size = UEBCLX;
104         if (size > ep->end-ep->buf)
105                 return 0;
106         while (size--)
107                 *ep->buf++ = UEDATX;
108         if (ep->buf == ep->end) {
109                 ep->state = EP_IDLE;
110                 if (ep->callback)
111                         ep->callback(ep->user);
112 //              if (ep == &eps[0])
113                         usb_send(ep, NULL, 0, NULL, NULL);
114         }
115         return 1;
116 }
117
118
119 static void ep_tx(struct ep_descr *ep)
120 {
121         uint8_t size = ep->end-ep->buf;
122         uint8_t left;
123
124         if (size > ep->size)
125                 size = ep->size;
126         for (left = size; left; left--)
127                 UEDATX = *ep->buf++;
128         if (size == ep->size)
129                 return;
130         ep->state = EP_IDLE;
131 }
132
133
134 static void handle_ep(int n)
135 {
136         struct ep_descr *ep = eps+n;
137         uint8_t mask;
138
139         UENUM = n;
140         if (UEINTX & (1 << RXSTPI)) {
141                 /* @@@ EP_RX. EP_TX: cancel */
142                 ep->state = EP_IDLE;
143                 if (!ep_setup())
144                         goto stall;
145                 UEINTX = ~(1 << RXSTPI);
146         }
147         if (UEINTX & (1 << RXOUTI)) {
148                 /* @@ EP_TX: cancel */
149                 if (ep->state != EP_RX)
150                         goto stall;
151                 if (!ep_rx(ep))
152                         goto stall;
153                 /* @@@ gcc 4.5.2 wants this cast */
154                 UEINTX = (uint8_t) ~(1 << RXOUTI | 1 << FIFOCON);
155         }
156         if (UEINTX & (1 << STALLEDI)) {
157                 ep->state = EP_IDLE;
158                 UEINTX = ~(1 << STALLEDI);
159         }
160         if (UEINTX & (1 << TXINI)) {
161                 /* @@ EP_RX: cancel (?) */
162                 if (ep->state == EP_TX) {
163                         ep_tx(ep);
164                         mask = 1 << TXINI;
165                         if (n)
166                                 mask |= 1 << FIFOCON;
167                         UEINTX = ~mask;
168                         if (ep->state == EP_IDLE && ep->callback)
169                                 ep->callback(ep->user);
170                 } else {
171                         UEIENX &= ~(1 << TXINE);
172                 }
173         }
174         return;
175
176 stall:
177         UEINTX = ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI);
178         ep->state = EP_IDLE;
179         UECONX |= 1 << STALLRQ;
180 }
181
182
183 void ep_init(void)
184 {
185         UENUM = 0;
186         UECONX = (1 << RSTDT) | (1 << EPEN);    /* enable */
187         UECFG0X = 0;    /* control, direction is ignored */
188         UECFG1X = 3 << EPSIZE0; /* 64 bytes */
189         UECFG1X |= 1 << ALLOC;
190
191         while (!(UESTA0X & (1 << CFGOK)));
192
193         UEIENX =
194             (1 << RXSTPE) | (1 << RXOUTE) | (1 << STALLEDE) | (1 << TXINE);
195
196         eps[0].state = EP_IDLE;
197         eps[0].size = 64;
198
199 #ifndef BOOT_LOADER
200
201         UENUM = 1;
202         UECONX = (1 << RSTDT) | (1 << EPEN);    /* enable */
203         UECFG0X = (1 << EPTYPE1) | (1 << EPDIR); /* bulk IN */
204         UECFG1X = 3 << EPSIZE0; /* 64 bytes */
205         UECFG1X |= 1 << ALLOC;
206
207         while (!(UESTA0X & (1 << CFGOK)));
208
209         UEIENX = (1 << STALLEDE) | (1 << TXINE);
210
211         eps[1].state = EP_IDLE;
212         eps[1].size = 64;
213
214 #endif
215 }
216
217
218 ISR(USB_GEN_vect)
219 {
220         uint8_t flags;
221
222         flags = UDINT;
223         if (flags & (1 << EORSTI)) {
224                 if (user_reset)
225                         user_reset();
226                 ep_init();
227                 UDINT = ~(1 << EORSTI);
228         }
229 }
230
231
232 ISR(USB_COM_vect)
233 {
234         uint8_t flags, i;
235
236         flags = UEINT;
237         for (i = 0; i != NUM_EPS; i++)
238                 if (flags & (1 << i))
239                         handle_ep(i);
240 }
241
242
243 void usb_reset(void)
244 {
245         UDCON |= 1 << DETACH;   /* detach the pull-up */
246         _delay_ms(1);
247 }