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