C8051F32x firmware infrastructure

Sign in or create your account | Project List | Help

C8051F32x firmware infrastructure Git Source Tree

Root/fw/common/usb.c

1/*
2 * common/usb.c - USB hardware setup and standard device requests
3 *
4 * Written 2008-2010 by Werner Almesberger
5 * Copyright 2008-2010 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 * - EP0-sized packets cause an (otherwise harmless) SUEND at the end of the
17 * packet
18 * - #ifdef hell
19 */
20
21/*
22 * This code follows the register read/write sequences from the examples in
23 * SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_Main.c and
24 * SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_ISR.c
25 *
26 * More resources:
27 * http://www.beyondlogic.org/usbnutshell/usb1.htm
28 */
29
30
31#include <stdint.h>
32
33#include "regs.h"
34#include "uart.h"
35#include "usb.h"
36
37
38#ifndef NULL
39#define NULL 0
40#endif
41
42
43#define BUG_ON(x)
44
45
46#define NO_ADDRESS 0xff /* null value for function address */
47
48
49__xdata struct ep_descr ep0;
50#ifdef CONFIG_EP1
51__xdata struct ep_descr ep1in, ep1out;
52#endif
53
54__bit (*user_setup)(struct setup_request *setup) __reentrant;
55__bit (*user_get_descriptor)(uint8_t type, uint8_t index,
56    const uint8_t * const *reply, uint8_t *size) __reentrant;
57void (*user_reset)(void) __reentrant;
58
59
60static uint8_t addr = NO_ADDRESS;
61
62
63void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
64    uint8_t size, void (*callback)(void *user), void *user)
65{
66    BUG_ON(ep->state);
67    ep->state = state;
68    ep->buf = buf;
69    ep->end = buf+size;
70    ep->callback = callback;
71    ep->user = user;
72}
73
74
75static void usb_write(uint8_t reg, uint8_t value)
76{
77    while (USB0ADR & BUSY);
78    USB0ADR = reg;
79    USB0DAT = value;
80}
81
82
83static uint8_t usb_read(uint8_t reg)
84{
85    while (USB0ADR & BUSY);
86    USB0ADR = reg | BUSY;
87    while (USB0ADR & BUSY);
88    return USB0DAT;
89}
90
91
92static uint16_t usb_read_word(uint8_t reg)
93{
94    uint8_t low;
95
96    low = usb_read(reg);
97    return low | usb_read(reg) << 8;
98}
99
100
101static __bit get_descriptor(uint8_t type, uint8_t index, uint16_t length)
102{
103    const uint8_t *reply;
104    uint8_t size;
105
106    debug("get_descriptor(0x%02x, 0x%02x, 0x%04x)\n", type, index, length);
107
108    switch (type) {
109    case USB_DT_DEVICE:
110        reply = device_descriptor;
111        size = reply[0];
112        break;
113    case USB_DT_CONFIG:
114        if (index)
115            return 0;
116        reply = config_descriptor;
117        size = reply[2];
118        break;
119    default:
120        if (!user_get_descriptor)
121            return 0;
122        if (!user_get_descriptor(type, index, &reply, &size))
123            return 0;
124    }
125    if (length < size)
126        size = length;
127    usb_send(&ep0, reply, size, NULL, NULL);
128    return 1;
129}
130
131
132/*
133 * Process a SETUP packet. Hardware ensures that length is 8 bytes.
134 */
135
136
137static void handle_setup(void)
138{
139    struct setup_request setup;
140    __bit ok = 0;
141
142    BUG_ON(usb_read(E0CNT) < 8);
143
144    setup.bmRequestType = usb_read(FIFO0);
145    setup.bRequest = usb_read(FIFO0);
146    setup.wValue = usb_read_word(FIFO0);
147    setup.wIndex = usb_read_word(FIFO0);
148    setup.wLength = usb_read_word(FIFO0);
149
150    switch (setup.bmRequestType | setup.bRequest << 8) {
151
152    /*
153     * Device request
154     *
155     * See http://www.beyondlogic.org/usbnutshell/usb6.htm
156     */
157
158    case FROM_DEVICE(GET_STATUS):
159        debug("GET_STATUS\n");
160        if (setup.wLength != 2)
161            goto stall;
162        usb_send(&ep0, "\000", 2, NULL, NULL);
163        ok = 1;
164        break;
165    case TO_DEVICE(CLEAR_FEATURE):
166        debug("CLEAR_FEATURE\n");
167        ok = 1;
168        break;
169    case TO_DEVICE(SET_FEATURE):
170        debug("SET_FEATURE\n");
171        break;
172    case TO_DEVICE(SET_ADDRESS):
173        debug("SET_ADDRESS (0x%x)\n", setup.wValue);
174        addr = setup.wValue;
175        ok = 1;
176        break;
177    case FROM_DEVICE(GET_DESCRIPTOR):
178        ok = get_descriptor(setup.wValue >> 8, setup.wValue,
179            setup.wLength);
180        break;
181    case TO_DEVICE(SET_DESCRIPTOR):
182        error("SET_DESCRIPTOR\n");
183        break;
184    case FROM_DEVICE(GET_CONFIGURATION):
185        debug("GET_CONFIGURATION\n");
186        usb_send(&ep0, "", 1, NULL, NULL);
187        ok = 1;
188        break;
189    case TO_DEVICE(SET_CONFIGURATION):
190        debug("SET_CONFIGURATION\n");
191        ok = setup.wValue == config_descriptor[5];
192        break;
193
194    /*
195     * Interface request
196     */
197
198    case FROM_INTERFACE(GET_STATUS):
199        printk("GET_STATUS\n");
200        break;
201    case TO_INTERFACE(CLEAR_FEATURE):
202        printk("CLEAR_FEATURE\n");
203        break;
204    case TO_INTERFACE(SET_FEATURE):
205        printk("SET_FEATURE\n");
206        break;
207    case FROM_INTERFACE(GET_INTERFACE):
208        printk("GET_INTERFACE\n");
209        break;
210    case TO_INTERFACE(SET_INTERFACE):
211        debug("SET_INTERFACE\n");
212        {
213            uint8_t *interface_descriptor = config_descriptor+9;
214
215            ok = setup.wIndex == interface_descriptor[2] &&
216                setup.wValue == interface_descriptor[3];
217        }
218        break;
219
220    /*
221     * Endpoint request
222     */
223
224    case FROM_ENDPOINT(GET_STATUS):
225        printk("GET_STATUS\n");
226        break;
227    case TO_ENDPOINT(CLEAR_FEATURE):
228        printk("CLEAR_FEATURE(EP)\n");
229        break;
230    case TO_ENDPOINT(SET_FEATURE):
231        printk("SET_FEATURE(EP)\n");
232        break;
233    case FROM_ENDPOINT(SYNCH_FRAME):
234        printk("SYNCH_FRAME\n");
235        break;
236
237    default:
238        if (user_setup) {
239            ok = user_setup(&setup);
240            /*
241             * If we "break" here instead of "goto", "EVELYN the
242             * modified DOG" gets furious, says SDCC.
243             */
244            if (ok)
245                goto okay;
246        }
247        printk("Unrecognized SETUP(%02x %02x ...\n",
248            setup.bmRequestType, setup.bRequest);
249    }
250
251    if (ok) {
252okay:
253        if ((setup.bmRequestType & 0x80) || ep0.state == EP_RX)
254            usb_write(E0CSR, SOPRDY);
255        else
256            usb_write(E0CSR, SOPRDY | DATAEND);
257        return;
258    }
259stall:
260    printk("STALL\n");
261    /* AN139 recommends this sequence */
262    usb_write(E0CSR, SOPRDY | SDSTL_0);
263    ep0.state = EP_IDLE;
264}
265
266
267static void ep0_data(void)
268{
269    uint8_t fifo;
270
271    fifo = usb_read(E0CNT);
272    if (fifo > ep0.end-ep0.buf) {
273        usb_write(E0CSR, SDSTL_0);
274        return;
275    }
276    while (fifo--)
277        *ep0.buf++ = usb_read(FIFO0);
278    if (ep0.buf == ep0.end) {
279        ep0.state = EP_IDLE;
280        if (ep0.callback)
281            ep0.callback(ep0.user);
282    }
283    if (ep0.state == EP_IDLE)
284        usb_write(E0CSR, SOPRDY | DATAEND);
285    else
286        usb_write(E0CSR, SOPRDY);
287}
288
289
290static void handle_ep0(void)
291{
292    uint8_t csr, size, left;
293
294    if (addr != NO_ADDRESS) {
295        usb_write(FADDR, addr);
296        debug("A");
297        addr = NO_ADDRESS;
298    }
299
300    csr = usb_read(E0CSR);
301
302    /* clear sent stall indication */
303    if (csr & STSTL_0) {
304        usb_write(E0CSR, 0);
305        /*
306         * @@@ Should return to IDLE, but this causes confusion with
307         * OPRDY_0. Need to investigate.
308         * ep0.state = EP_IDLE;
309         */
310    }
311
312    /* if transaction was interrupted, clean up */
313    if (csr & SUEND) {
314        debug("S");
315        usb_write(E0CSR, DATAEND | SSUEND);
316        ep0.state = EP_IDLE;
317    }
318
319    if (csr & OPRDY_0) {
320        switch (ep0.state) {
321        case EP_IDLE:
322            handle_setup();
323            break;
324        case EP_RX:
325            ep0_data();
326            break;
327        default:
328            printk("??? %d\n", ep0.state);
329            break;
330        }
331    }
332
333    if (ep0.state != EP_TX)
334        return;
335
336    csr = usb_read(E0CSR);
337    if (csr & INPRDY_0)
338        return;
339    if (csr & (SUEND | OPRDY_0))
340        return;
341
342    size = ep0.end-ep0.buf;
343    if (size > EP0_SIZE)
344        size = EP0_SIZE;
345    for (left = size; left; left--)
346        usb_write(FIFO0, *ep0.buf++);
347
348    csr |= INPRDY_0;
349    if (size != EP0_SIZE) {
350        ep0.state = EP_IDLE;
351        csr |= DATAEND;
352    }
353
354    usb_write(E0CSR, csr);
355
356    if (ep0.state == EP_IDLE && ep0.callback)
357        ep0.callback(ep0.user);
358}
359
360
361#ifdef CONFIG_EP1
362
363static void handle_ep1in(void)
364{
365    uint8_t csrl;
366
367    csrl = usb_read(EINCSRL);
368    debug("handle_ep1in: EINCSRL 0x%02x\n", csrl);
369    if (csrl & UNDRUN)
370        csrl &= ~UNDRUN;
371    if (csrl & STSTL_IN)
372        csrl &= ~STSTL_IN;
373
374    usb_write(EINCSRL, csrl);
375}
376
377
378static void fill_ep1in(void)
379{
380    uint8_t csrl, left;
381    uint16_t size;
382
383    if (ep1in.state != EP_TX)
384        return;
385
386    csrl = usb_read(EINCSRL);
387    debug("fill_ep1in: EINCSRL 0x%02x\n", csrl);
388    if (csrl & FIFONE)
389        return;
390
391    size = ep1in.end-ep1in.buf;
392    if (size > EP1_SIZE)
393        size = EP1_SIZE;
394    for (left = size; left; left--)
395        usb_write(FIFO1, *ep1in.buf++);
396    if (size != EP1_SIZE)
397        ep1in.state = EP_IDLE;
398    csrl |= INPRDY_IN;
399
400    usb_write(EINCSRL, csrl);
401
402    if (ep1in.state == EP_IDLE && ep1in.callback)
403        ep1in.callback(ep1in.user);
404}
405
406
407static void handle_ep1out(void)
408{
409    uint8_t csrl, fifo;
410
411    csrl = usb_read(EOUTCSRL);
412    debug("EOUTCSRL 0x%02x\n", csrl);
413    if (csrl & OVRUN)
414        csrl &= ~OVRUN;
415    if (csrl & STSTL_OUT) {
416        csrl &= ~STSTL_OUT;
417        csrl |= CLRDT_OUT;
418    }
419
420    usb_write(EINCSRL, csrl);
421
422    if (!(csrl & OPRDY_OUT))
423        return;
424
425    if (ep1out.state != EP_RX) {
426        usb_write(EOUTCSRL, FLUSH_OUT);
427        return;
428    }
429
430    fifo = usb_read(EOUTCNTL);
431    if (fifo > ep1out.end-ep1out.buf) {
432        usb_write(EOUTCSRL, SDSTL_OUT | FLUSH_OUT);
433        return;
434    }
435    while (fifo--)
436        *ep1out.buf++ = usb_read(FIFO1);
437
438    ep1out.state = EP_IDLE;
439    if (ep1out.callback)
440        ep1out.callback(ep1out.user);
441
442    csrl &= ~OPRDY_OUT;
443    usb_write(EOUTCSRL, csrl);
444}
445
446#endif /* CONFIG_EP1 */
447
448
449void usb_poll(void)
450{
451    uint8_t flags;
452
453    flags = usb_read(CMINT);
454    if (flags) {
455        debug("CMINT 0x%02x\n", flags);
456        if (flags & RSTINT) {
457            ep0.state = EP_IDLE;
458            /*
459             * EP state serves as "buffer is valid" indicator for
460             * EP1OUT, so don't reset it. We need to call back
461             * EP1IN to tell it that the URB can be reused.
462             *
463             * (@@@ does this make sense ?)
464             */
465#ifdef CONFIG_EP1
466            if (ep1in.state == EP_TX && ep1in.callback)
467                ep1in.callback(ep1in.user);
468                
469            ep1in.state = EP_IDLE;
470#endif
471            usb_write(POWER, 0);
472            if (user_reset)
473                user_reset();
474                /* @@@ 1 for suspend signaling */
475        }
476    }
477
478    flags = usb_read(IN1INT);
479    if (flags) {
480        debug("IN1INT 0x%02x\n", flags);
481        if (flags & EP0) {
482            usb_write(INDEX, 0);
483            handle_ep0();
484        }
485#ifdef CONFIG_EP1
486        if (flags & IN1) {
487            usb_write(INDEX, 1);
488            handle_ep1in();
489        }
490#endif
491    }
492#ifdef CONFIG_EP1
493    usb_write(INDEX, 1);
494    fill_ep1in();
495#endif
496
497#ifdef CONFIG_EP1
498    flags = usb_read(OUT1INT);
499    if (flags) {
500        debug("OUT1INT 0x%02x\n", flags);
501        usb_write(INDEX, 1);
502        handle_ep1out();
503    }
504#endif
505}
506
507
508void usb_init(void)
509{
510    usb_write(POWER, USBRST);
511#ifdef LOW_SPEED
512    USB0XCN = PHYEN | PREN;
513    usb_write(CLKREC, CRE | CRLOW);
514#else
515    USB0XCN = PHYEN | PREN | SPEED;
516    usb_write(CLKREC, CRE);
517#endif
518    //usb_write(POWER, 0x01); /* we don't implement suspend yet */
519    usb_write(POWER, 0x00);
520}
521

Archive Download this file

Branches:
master



interactive