usb.c

来自「非常全的nrf2401设计资料」· C语言 代码 · 共 432 行

C
432
字号
/* Copyright (c) 2007 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRENTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 * $LastChangedRevision: 2290 $
 */

/** @file
 * Minimalistic USB code for the bootloader.
 *
 * @author Ken A. Redergaard
 * @author Ole Saether
 *
 */
#include <Nordic\reg24lu1.h>
#include <intrins.h>
#include <stdbool.h>

#include "config.h"
#include "usb.h"

#ifdef USE_USERCLASS
#pragma userclass (code = BOOTLOADER)
#pragma userclass (const = BOOTLOADER)
#endif

/** Leaves the minimum of the two arguments */
#define MIN(a, b) ((a) < (b) ? (a) : (b))

// USB map:
xdata volatile uint8_t out1buf[USB_EP1_SIZE]        _at_ 0xC640;
xdata volatile uint8_t in1buf[USB_EP1_SIZE]         _at_ 0xC680;
xdata volatile uint8_t out0buf[MAX_PACKET_SIZE_EP0] _at_ 0xC6C0;
xdata volatile uint8_t in0buf[MAX_PACKET_SIZE_EP0]  _at_ 0xC700;
xdata volatile uint8_t bout1addr                    _at_ 0xC781;
xdata volatile uint8_t bout2addr                    _at_ 0xC782;
xdata volatile uint8_t bout3addr                    _at_ 0xC783;
xdata volatile uint8_t bout4addr                    _at_ 0xC784;
xdata volatile uint8_t bout5addr                    _at_ 0xC785;
xdata volatile uint8_t binstaddr                    _at_ 0xC788;
xdata volatile uint8_t bin1addr                     _at_ 0xC789;
xdata volatile uint8_t bin2addr                     _at_ 0xC78A;
xdata volatile uint8_t bin3addr                     _at_ 0xC78B;
xdata volatile uint8_t bin4addr                     _at_ 0xC78C;
xdata volatile uint8_t bin5addr                     _at_ 0xC78D;
xdata volatile uint8_t ivec                         _at_ 0xC7A8;
xdata volatile uint8_t in_irq                       _at_ 0xC7A9;
xdata volatile uint8_t out_irq                      _at_ 0xC7AA;
xdata volatile uint8_t usbirq                       _at_ 0xC7AB;
xdata volatile uint8_t in_ien                       _at_ 0xC7AC;
xdata volatile uint8_t out_ien                      _at_ 0xC7AD;
xdata volatile uint8_t usbien                       _at_ 0xC7AE;
xdata volatile uint8_t ep0cs                        _at_ 0xC7B4;
xdata volatile uint8_t in0bc                        _at_ 0xC7B5;
xdata volatile uint8_t in1cs                        _at_ 0xC7B6;
xdata volatile uint8_t in1bc                        _at_ 0xC7B7;
xdata volatile uint8_t out0bc                       _at_ 0xC7C5;
xdata volatile uint8_t out1cs                       _at_ 0xC7C6;
xdata volatile uint8_t out1bc                       _at_ 0xC7C7;
xdata volatile uint8_t usbcs                        _at_ 0xC7D6;
xdata volatile uint8_t inbulkval                    _at_ 0xC7DE;
xdata volatile uint8_t outbulkval                   _at_ 0xC7DF;
xdata volatile uint8_t inisoval                     _at_ 0xC7E0;
xdata volatile uint8_t outisoval                    _at_ 0xC7E1;
xdata volatile uint8_t setupbuf[8]                  _at_ 0xC7E8;

static uint8_t usb_bm_state;
static uint8_t usb_current_config;
static uint8_t usb_current_alt_interface;
static usb_state_t usb_state;

static uint8_t code * packetizer_data_ptr;
static uint8_t packetizer_data_size;
static uint8_t packetizer_pkt_size;
static uint8_t bmRequestType;

bool packet_received;

static void packetizer_isr_ep0_in();
static void usb_process_get_status();
static void usb_process_get_descriptor();

static void delay_ms(uint16_t ms)
{
    uint16_t i, j;
    
    for(i = 0; i < ms; i++ )
    {
        for( j = 0; j < 1403; j++)
        {
            _nop_();
        }
    }
}

void usb_init(void)
{
    // Setup state information
    usb_state = DEFAULT;
    usb_bm_state = 0;

    // Setconfig configuration information
    usb_current_config = 0;
    usb_current_alt_interface = 0;
    
    // Disconnect from USB-bus since we are in this routine from a power on and not a soft reset:

    usbcs |= 0x08;
    delay_ms(50);
    usbcs &= ~0x08;

    usbien = 0x1d;
    in_ien = 0x01;
    in_irq = 0x1f;
    out_ien = 0x01;
    out_irq = 0x1f;

    // Setup the USB RAM with some OK default values:
    bout1addr = MAX_PACKET_SIZE_EP0/2;
    bout2addr = MAX_PACKET_SIZE_EP0/2 + USB_EP1_SIZE/2;
    bout3addr = MAX_PACKET_SIZE_EP0/2 + 2*USB_EP1_SIZE/2;
    bout4addr = MAX_PACKET_SIZE_EP0/2 + 3*USB_EP1_SIZE/2;
    bout5addr = MAX_PACKET_SIZE_EP0/2 + 4*USB_EP1_SIZE/2;
    binstaddr = 0xc0;
    bin1addr = MAX_PACKET_SIZE_EP0/2;
    bin2addr = MAX_PACKET_SIZE_EP0/2 + USB_EP1_SIZE/2;
    bin3addr = MAX_PACKET_SIZE_EP0/2 + 2*USB_EP1_SIZE/2;
    bin4addr = MAX_PACKET_SIZE_EP0/2 + 3*USB_EP1_SIZE/2;
    bin5addr = MAX_PACKET_SIZE_EP0/2 + 4*USB_EP1_SIZE/2;

    // Set all endpoints to not valid (except EP0IN and EP0OUT)
    inbulkval = 0x01;
    outbulkval = 0x01;
    inisoval = 0x00;
    outisoval = 0x00;

    in_ien |= 0x02;; 
    inbulkval |= 0x02;
    out_ien |= 0x02;
    outbulkval |= 0x02;
    out1bc = 0xff;
}

static void packetizer_isr_ep0_in()
{
    uint8_t code* data_ptr; 
    uint8_t size, i;
    // We are getting a ep0in interupt when the host send ACK and do not have any more data to send
    if(packetizer_data_size == 0)
    {
        in0bc = 0;
        USB_EP0_HSNAK();
        return;
    }

    size = MIN(packetizer_data_size, packetizer_pkt_size);

    // Copy data to the USB-controller buffer
    data_ptr = packetizer_data_ptr;
    for(i = 0; i < size;i++)
    {
        in0buf[i] = *data_ptr++;
    }

    // Tell the USB-controller how many bytes to send
    // If a IN is received from host after this the USB-controller will send the data
    in0bc = size;

    // Update the packetizer data
    packetizer_data_ptr += size;
    packetizer_data_size -= size;
}

static void usb_process_get_status()
{
    in0buf[0] = in0buf[1] = 0x00;
    if((usb_state == ADDRESSED) && (setupbuf[4] == 0x00))
    {
        in0bc = 0x02;
    }
    else if(usb_state == CONFIGURED)
    {
        switch(bmRequestType)
        {
            case 0x80: // Device
                if((usb_bm_state & USB_BM_STATE_ALLOW_REMOTE_WAKEUP ) == USB_BM_STATE_ALLOW_REMOTE_WAKEUP)
                {
                    in0buf[0] = 0x02;
                }
                in0bc = 0x02;
                break;

            case 0x81: // Interface
                in0bc = 0x02;
                break;

            case 0x82: // Endpoint
                if((setupbuf[4] & 0x80) == 0x80) // IN endpoints
                    in0buf[0] = in1cs;
                else
                    in0buf[0] = out1cs;
                in0bc = 0x02;
                break;
            default:
                USB_EP0_STALL();
                break;
        }
    }
    else
    {
        // We should not be in this state
        USB_EP0_STALL();
    }
}

static void usb_process_get_descriptor()
{ 
    packetizer_pkt_size = MAX_PACKET_SIZE_EP0;
    // Switch on descriptor type
    switch(setupbuf[3])
    {
        case USB_DESC_DEVICE:
            packetizer_data_ptr = (uint8_t*)&g_usb_dev_desc;
            packetizer_data_size = MIN(setupbuf[6], sizeof(usb_dev_desc_t));
            packetizer_isr_ep0_in();
            break;

        case USB_DESC_CONFIGURATION:
            // For now we just support one configuration. The asked configuration is stored in LSB(wValue).
            packetizer_data_ptr = (uint8_t*)&g_usb_conf_desc;
            packetizer_data_size = MIN(setupbuf[6], sizeof(usb_conf_desc_bootloader_t));
            packetizer_isr_ep0_in();
            break;

        case USB_DESC_STRING:
            // For now we just support english as string descriptor language.
            if(setupbuf[2] == 0x00)
            {
                packetizer_data_ptr = string_zero;
                packetizer_data_size = MIN(setupbuf[6], sizeof(string_zero));
                packetizer_isr_ep0_in();
            }
            else
            {
                if((setupbuf[2] - 1) < USB_STRING_DESC_COUNT)
                {
                    if (setupbuf[2] == 1)
                        packetizer_data_ptr = g_usb_string_desc_1;
                    else
                        packetizer_data_ptr = g_usb_string_desc_2;;
                    packetizer_data_size = MIN(setupbuf[6], packetizer_data_ptr[0]);
                    packetizer_isr_ep0_in();
                }
                else
                {
                    USB_EP0_STALL();
                }
            }
            break;
        case USB_DESC_INTERFACE:
        case USB_DESC_ENDPOINT:
        case USB_DESC_DEVICE_QUAL:
        case USB_DESC_OTHER_SPEED_CONF:
        case USB_DESC_INTERFACE_POWER:
            USB_EP0_STALL();
            break;
        default:
            USB_EP0_HSNAK();
            break;
    }
}

static void isr_sudav()
{
    bmRequestType = setupbuf[0];
    if((bmRequestType & 0x60 ) == 0x00)
    {
        switch(setupbuf[1])
        {
           case USB_REQ_GET_DESCRIPTOR:
               usb_process_get_descriptor();
               break;

           case USB_REQ_GET_STATUS:
               usb_process_get_status();
               break;

            case USB_REQ_SET_ADDRESS:
               usb_state = ADDRESSED;
               usb_current_config = 0x00;
               break;

            case USB_REQ_GET_CONFIGURATION:
                switch(usb_state)
                {
                    case ADDRESSED:
                        in0buf[0] = 0x00;
                        in0bc = 0x01;
                        break;
                    case CONFIGURED:
                        in0buf[0] = usb_current_config;
                        in0bc = 0x01;
                        break;
                    case ATTACHED:
                    case POWERED:
                    case SUSPENDED:
                    case DEFAULT:
                    default:
                        USB_EP0_STALL();
                        break;
                }
                break;

            case USB_REQ_SET_CONFIGURATION:
                switch(setupbuf[2])
                {
                    case 0x00:
                        usb_state = ADDRESSED;
                        usb_current_config = 0x00;
                        USB_EP0_HSNAK();
                        break;
                    case 0x01:
                        usb_state = CONFIGURED;
                        usb_bm_state |= USB_BM_STATE_CONFIGURED;
                        usb_current_config = 0x01;
                        USB_EP0_HSNAK();
                        break;
                    default:
                        USB_EP0_STALL();
                        break;
                }
               break;

            case USB_REQ_GET_INTERFACE: // GET_INTERFACE
                in0buf[0] = usb_current_alt_interface;
                in0bc = 0x01;
                break;

            case USB_REQ_SET_DESCRIPTOR:
            case USB_REQ_SET_INTERFACE: // SET_INTERFACE
            case USB_REQ_SYNCH_FRAME:   // SYNCH_FRAME
            default:
                USB_EP0_STALL();
                break;
        }
    } 
    // bmRequestType = 0 01 xxxxx : Data transfer direction: Host-to-device, Type: Class
    else if((bmRequestType & 0x60 ) == 0x20)  // Class request
    {
        if(setupbuf[6] != 0 && ((bmRequestType & 0x80) == 0x00))
        {
            // If there is a OUT-transaction associated with the Control-Transfer-Write we call the callback
            // when the OUT-transaction is finished. Note that this function do not handle several out transactions.
            out0bc = 0xff;
        }
        else
        {
            USB_EP0_HSNAK();
        }
    } 
    else  // Unknown request type
    {
        USB_EP0_STALL();
    }
}

void usb_irq(void)
{
    //
    // Split case into an if and a switch to force Keil into not using a library function:
    if (ivec == INT_USBRESET)
    {
        usbirq = 0x10;
        usb_state = DEFAULT;
        usb_current_config = 0;
        usb_current_alt_interface = 0;
        usb_bm_state = 0;
    }
    else
    {
        switch(ivec)
        {
            case INT_SUDAV:
                usbirq = 0x01;
                isr_sudav();
                break;
            case INT_SOF:
                usbirq = 0x02;
                break;
            case INT_SUTOK:
                usbirq = 0x04;
                packetizer_data_ptr = NULL;
                packetizer_data_size = 0;
                packetizer_pkt_size = 0;
                break;
            case INT_SUSPEND:
                usbirq = 0x08;
                break;
            case INT_EP0IN:
                in_irq = 0x01;
                packetizer_isr_ep0_in();
                break;
            case INT_EP0OUT:
                out_irq = 0x01;
                packetizer_data_size = 0;
                //        req.misc_data = out0buf;
                USB_EP0_HSNAK();
                break;
            case INT_EP1IN:
                // Clear interrupt 
                in_irq = 0x02;
                in1cs = 0x02;
                break;
            case INT_EP1OUT:
                // Clear interrupt
                out_irq = 0x02;     
                packet_received = true;
                out1bc = 0xff;
                break;
            default:
                break;
        }
    }
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?