Add preliminary aoview code
[fw/altos] / ao_usb.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19 #include "ao_usb.h"
20
21 struct ao_task __xdata ao_usb_task;
22
23 static __xdata uint16_t ao_usb_in_bytes;
24 static __xdata uint16_t ao_usb_out_bytes;
25 static __xdata uint8_t  ao_usb_iif;
26 static __xdata uint8_t  ao_usb_running;
27
28 static void
29 ao_usb_set_interrupts(void)
30 {
31         /* IN interrupts on the control an IN endpoints */
32         USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
33
34         /* OUT interrupts on the OUT endpoint */
35         USBOIE = (1 << AO_USB_OUT_EP);
36
37         /* Only care about reset */
38         USBCIE = USBCIE_RSTIE;
39 }
40
41 /* This interrupt is shared with port 2, 
42  * so when we hook that up, fix this
43  */
44 void
45 ao_usb_isr(void) interrupt 6
46 {
47         USBIF = 0;
48         ao_usb_iif |= USBIIF;
49         if (ao_usb_iif & 1)
50                 ao_wakeup(&ao_usb_task);
51         if (ao_usb_iif & (1 << AO_USB_IN_EP))
52                 ao_wakeup(&ao_usb_in_bytes);
53
54         if (USBOIF & (1 << AO_USB_OUT_EP))
55                 ao_wakeup(&ao_usb_out_bytes);
56
57         if (USBCIF & USBCIF_RSTIF)
58                 ao_usb_set_interrupts();
59 }
60
61 struct ao_usb_setup {
62         uint8_t         dir_type_recip;
63         uint8_t         request;
64         uint16_t        value;
65         uint16_t        index;
66         uint16_t        length;
67 } __xdata ao_usb_setup;
68
69 __xdata uint8_t ao_usb_ep0_state;
70 uint8_t * __xdata ao_usb_ep0_in_data;
71 __xdata uint8_t ao_usb_ep0_in_len;
72 __xdata uint8_t ao_usb_ep0_in_buf[2];
73 __xdata uint8_t ao_usb_ep0_out_len;
74 __xdata uint8_t *__data ao_usb_ep0_out_data;
75 __xdata uint8_t ao_usb_configuration;
76
77 /* Send an IN data packet */
78 static void
79 ao_usb_ep0_flush(void)
80 {
81         __xdata uint8_t this_len;
82         __xdata uint8_t cs0;
83         
84         USBINDEX = 0;
85         cs0 = USBCS0;
86         if (cs0 & USBCS0_INPKT_RDY)
87                 ao_panic(0);
88
89         this_len = ao_usb_ep0_in_len;
90         if (this_len > AO_USB_CONTROL_SIZE)
91                 this_len = AO_USB_CONTROL_SIZE;
92         cs0 = USBCS0_INPKT_RDY;
93         if (this_len != AO_USB_CONTROL_SIZE) {
94                 cs0 = USBCS0_INPKT_RDY | USBCS0_DATA_END;
95                 ao_usb_ep0_state = AO_USB_EP0_IDLE;
96         }
97         ao_usb_ep0_in_len -= this_len;
98         while (this_len--)
99                 USBFIFO[0] = *ao_usb_ep0_in_data++;
100         USBINDEX = 0;
101         USBCS0 = cs0;
102 }
103
104 __xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
105
106 /* Walk through the list of descriptors and find a match
107  */
108 static void
109 ao_usb_get_descriptor(uint16_t value)
110 {
111         const uint8_t           *__xdata descriptor;
112         __xdata uint8_t         type = value >> 8;
113         __xdata uint8_t         index = value;
114
115         descriptor = ao_usb_descriptors;
116         while (descriptor[0] != 0) {
117                 if (descriptor[1] == type && index-- == 0) {
118                         if (type == AO_USB_DESC_CONFIGURATION)
119                                 ao_usb_ep0_in_len = descriptor[2];
120                         else
121                                 ao_usb_ep0_in_len = descriptor[0];
122                         ao_usb_ep0_in_data = descriptor;
123                         break;
124                 }
125                 descriptor += descriptor[0];
126         }
127 }
128
129 /* Read data from the ep0 OUT fifo
130  */
131 static void
132 ao_usb_ep0_fill(void)
133 {
134         __xdata uint8_t len;
135         
136         USBINDEX = 0;
137         len = USBCNT0;
138         if (len > ao_usb_ep0_out_len)
139                 len = ao_usb_ep0_out_len;
140         ao_usb_ep0_out_len -= len;
141         while (len--)
142                 *ao_usb_ep0_out_data++ = USBFIFO[0];
143 }
144
145 void
146 ao_usb_ep0_queue_byte(uint8_t a)
147 {
148         ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
149 }
150
151 void
152 ao_usb_set_address(uint8_t address)
153 {
154         ao_usb_running = 1;
155         USBADDR = address | 0x80;
156         while (USBADDR & 0x80)
157                 ;
158 }
159
160 static void
161 ao_usb_set_configuration(void)
162 {
163         /* Set the IN max packet size, double buffered */
164         USBINDEX = AO_USB_IN_EP;
165         USBMAXI = AO_USB_IN_SIZE >> 3;
166         USBCSIH |= USBCSIH_IN_DBL_BUF;
167
168         /* Set the OUT max packet size, double buffered */
169         USBINDEX = AO_USB_OUT_EP;
170         USBMAXO = AO_USB_OUT_SIZE >> 3;
171         USBCSOH = USBCSOH_OUT_DBL_BUF;
172 }
173
174 static void
175 ao_usb_ep0_setup(void)
176 {
177         /* Pull the setup packet out of the fifo */
178         ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
179         ao_usb_ep0_out_len = 8;
180         ao_usb_ep0_fill();
181         if (ao_usb_ep0_out_len != 0)
182                 return;
183
184         /* Figure out how to ACK the setup packet */
185         if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
186                 if (ao_usb_setup.length)
187                         ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
188                 else
189                         ao_usb_ep0_state = AO_USB_EP0_IDLE;
190         } else {
191                 if (ao_usb_setup.length)
192                         ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
193                 else
194                         ao_usb_ep0_state = AO_USB_EP0_IDLE;
195         }
196         USBINDEX = 0;
197         if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
198                 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
199         else
200                 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
201         
202         ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
203         ao_usb_ep0_in_len = 0;
204         switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
205         case AO_USB_TYPE_STANDARD:
206                 switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
207                 case AO_USB_RECIP_DEVICE:
208                         switch(ao_usb_setup.request) {
209                         case AO_USB_REQ_GET_STATUS:
210                                 ao_usb_ep0_queue_byte(0);
211                                 ao_usb_ep0_queue_byte(0);
212                                 break;
213                         case AO_USB_REQ_SET_ADDRESS:
214                                 ao_usb_set_address(ao_usb_setup.value);
215                                 break;
216                         case AO_USB_REQ_GET_DESCRIPTOR:
217                                 ao_usb_get_descriptor(ao_usb_setup.value);
218                                 break;
219                         case AO_USB_REQ_GET_CONFIGURATION:
220                                 ao_usb_ep0_queue_byte(ao_usb_configuration);
221                                 break;
222                         case AO_USB_REQ_SET_CONFIGURATION:
223                                 ao_usb_configuration = ao_usb_setup.value;
224                                 ao_usb_set_configuration();
225                                 break;
226                         }
227                         break;
228                 case AO_USB_RECIP_INTERFACE:
229                         #pragma disable_warning 110
230                         switch(ao_usb_setup.request) {
231                         case AO_USB_REQ_GET_STATUS:
232                                 ao_usb_ep0_queue_byte(0);
233                                 ao_usb_ep0_queue_byte(0);
234                                 break;
235                         case AO_USB_REQ_GET_INTERFACE:
236                                 ao_usb_ep0_queue_byte(0);
237                                 break;
238                         case AO_USB_REQ_SET_INTERFACE:
239                                 break;
240                         }
241                         break;
242                 case AO_USB_RECIP_ENDPOINT:
243                         switch(ao_usb_setup.request) {
244                         case AO_USB_REQ_GET_STATUS:
245                                 ao_usb_ep0_queue_byte(0);
246                                 ao_usb_ep0_queue_byte(0);
247                                 break;
248                         }
249                         break;
250                 }
251                 break;
252         case AO_USB_TYPE_CLASS:
253                 switch (ao_usb_setup.request) {
254                 case SET_LINE_CODING:
255                         ao_usb_ep0_out_len = 7;
256                         ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
257                         break;
258                 case GET_LINE_CODING:
259                         ao_usb_ep0_in_len = 7;
260                         ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
261                         break;
262                 case SET_CONTROL_LINE_STATE:
263                         break;
264                 }
265                 break;
266         }
267         if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
268                 if (ao_usb_setup.length < ao_usb_ep0_in_len)
269                         ao_usb_ep0_in_len = ao_usb_setup.length;
270                 ao_usb_ep0_flush();
271         }
272 }
273
274 /* End point 0 receives all of the control messages. */
275 static void
276 ao_usb_ep0(void)
277 {
278         __xdata uint8_t cs0;
279
280         ao_usb_ep0_state = AO_USB_EP0_IDLE;
281         for (;;) {
282                 __critical for (;;) {
283                         if (ao_usb_iif & 1) {
284                                 ao_usb_iif &= ~1;
285                                 break;
286                         }
287                         ao_sleep(&ao_usb_task);
288                 }
289                 USBINDEX = 0;
290                 cs0 = USBCS0;
291                 if (cs0 & USBCS0_SETUP_END) {
292                         ao_usb_ep0_state = AO_USB_EP0_IDLE;
293                         USBCS0 = USBCS0_CLR_SETUP_END;
294                 }
295                 if (cs0 & USBCS0_SENT_STALL) {
296                         ao_usb_ep0_state = AO_USB_EP0_IDLE;
297                         USBCS0 &= ~USBCS0_SENT_STALL;
298                 }
299                 if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
300                     (cs0 & USBCS0_INPKT_RDY) == 0)
301                 {
302                         ao_usb_ep0_flush();
303                 }
304                 if (cs0 & USBCS0_OUTPKT_RDY) {
305                         switch (ao_usb_ep0_state) {
306                         case AO_USB_EP0_IDLE:
307                                 ao_usb_ep0_setup();
308                                 break;
309                         case AO_USB_EP0_DATA_OUT:
310                                 ao_usb_ep0_fill();
311                                 if (ao_usb_ep0_out_len == 0)
312                                         ao_usb_ep0_state = AO_USB_EP0_IDLE;
313                                 USBINDEX = 0;
314                                 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
315                                         USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
316                                 else
317                                         USBCS0 = USBCS0_CLR_OUTPKT_RDY;
318                                 break;
319                         }
320                 }
321         }
322 }
323
324 void
325 ao_usb_flush(void) __critical
326 {
327         if (ao_usb_in_bytes) {
328                 USBINDEX = AO_USB_IN_EP;
329                 USBCSIL |= USBCSIL_INPKT_RDY;
330                 ao_usb_in_bytes = 0;
331         }
332 }
333
334 void
335 ao_usb_putchar(char c) __critical
336 {
337         if (!ao_usb_running)
338                 return;
339         for (;;) {
340                 USBINDEX = AO_USB_IN_EP;
341                 if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
342                         break;
343                 ao_sleep(&ao_usb_in_bytes);
344         }
345         USBFIFO[AO_USB_IN_EP << 1] = c;
346         if (++ao_usb_in_bytes == AO_USB_IN_SIZE) {
347                 USBINDEX = AO_USB_IN_EP;
348                 USBCSIL |= USBCSIL_INPKT_RDY;
349                 ao_usb_in_bytes = 0;
350         }
351 }
352
353 char
354 ao_usb_getchar(void) __critical
355 {
356         __xdata char    c;
357         while (ao_usb_out_bytes == 0) {
358                 for (;;) {
359                         USBINDEX = AO_USB_OUT_EP;
360                         if ((USBCSOL & USBCSOL_OUTPKT_RDY) != 0)
361                                 break;
362                         ao_sleep(&ao_usb_out_bytes);
363                 }
364                 ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
365         }
366         --ao_usb_out_bytes;
367         c = USBFIFO[AO_USB_OUT_EP << 1];
368         if (ao_usb_out_bytes == 0) {
369                 USBINDEX = AO_USB_OUT_EP;
370                 USBCSOL &= ~USBCSOL_OUTPKT_RDY;
371         }
372         return c;
373 }
374
375 void
376 ao_usb_enable(void)
377 {
378         /* Turn on the USB controller */
379         SLEEP |= SLEEP_USB_EN;
380
381         ao_usb_set_configuration();
382         
383         ao_usb_set_interrupts();
384
385         /* enable USB interrupts */
386         IEN2 |= IEN2_USBIE;
387
388         /* Clear any pending interrupts */
389         USBCIF = 0;
390         USBOIF = 0;
391         USBIIF = 0;
392 }
393
394 void
395 ao_usb_disable(void)
396 {
397         /* Disable USB interrupts */
398         USBIIE = 0;
399         USBOIE = 0;
400         USBCIE = 0;
401         IEN2 &= ~IEN2_USBIE;
402         
403         /* Clear any pending interrupts */
404         USBCIF = 0;
405         USBOIF = 0;
406         USBIIF = 0;
407
408         /* Turn off the USB controller */
409         SLEEP &= ~SLEEP_USB_EN;
410 }
411
412 void
413 ao_usb_init(void)
414 {
415         ao_usb_enable();
416
417         ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");
418 }