?? hal_usb.c
字號:
/* 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
* Implementaion of the USB HAL
* @author Ken A. Redergaard
*/
#include <intrins.h>
#include <stdint.h>
#include "nordic_common.h"
#include "hal_usb_desc.h"
#include "usb.h"
#include <Nordic\reg24lu1.h>
// Define formulas for jumping in the usb registry map based upon the endpoint number
// Calculate control and status register location in USB-controller
#define CALCULATE_CS_IN_PTR(ep) (uint8_t*)(&(i_usb.map->in1cs) + 2 * ( ( ep & 0x7f ) - 1 ))
#define CALCULATE_CS_OUT_PTR(ep) (uint8_t*)(&(i_usb.map->out1cs) + 2 * ( ( ep & 0x7f ) - 1 ))
// Calculate byte count register location in USB-controller
#define CALCULATE_BC_OUT_PTR(ep) (uint8_t*)(&(i_usb.map->out0bc) + ( ( ep ) * 2 ))
#define CALCULATE_BC_IN_PTR(ep) (uint8_t*)(&(i_usb.map->in0bc) + ( ( ep & 0x7f ) * 2 ))
// Calculate buffer location in USB-controller
#define CALCULATE_BUF_IN_PTR(ep) (uint8_t*)(i_usb.map->in0buf - ( ( ep & 0x7f ) * 128 ))
#define CALCULATE_BUF_OUT_PTR(ep) (uint8_t*)(i_usb.map->out0buf - ( ep * 128 ))
static xdata usb_t i_usb;
static hal_usb_device_req req;
hal_usb_t g_hal_usb;
static void packetize(uint8_t* data_ptr, uint8_t data_size, uint8_t pkt_size);
static void packetizer_isr_ep0_in();
static void usb_process_dev_req_cb_response(hal_usb_dev_req_resp_t ret, hal_usb_device_req* req, uint8_t* data_ptr, uint16_t size);
static void usb_process_get_status(hal_usb_device_req* req);
static void usb_process_get_descriptor(hal_usb_device_req* req);
static void isr_sudav();
static void isr_sof();
static void isr_sutok();
static void isr_suspend();
static void isr_usbreset();
static void isr_ep0out();
static void usb_process_ep_response(uint8_t ret, uint8_t* cs_ptr, uint8_t* bc_ptr);
static void delay_ms(uint8_t ms);
void hal_usb_init(
bool usb_disconnect,
hal_usb_cb_device_req_t device_req,
hal_usb_cb_reset_t reset,
hal_usb_cb_resume_t resume,
hal_usb_cb_suspend_t suspend)
{
// Setup the USB struct.
i_usb.map = (usb_map_t xdata*)0;
// Setup descriptors
g_hal_usb.descs.dev = &g_usb_dev_desc;
g_hal_usb.descs.dev = &g_usb_dev_desc;
g_hal_usb.descs.conf = &g_usb_conf_desc;
g_hal_usb.descs.string = &g_usb_string_desc;
// This is for setting language American English (String descriptor 0 is an array of supported languages)
g_hal_usb.descs.string_zero[0] = 0x04;
g_hal_usb.descs.string_zero[1] = 0x03;
g_hal_usb.descs.string_zero[2] = 0x09;
g_hal_usb.descs.string_zero[3] = 0x04;
// Setup state information
g_hal_usb.state = DEFAULT;
g_hal_usb.bm_state = 0;
// Setconfig configuration information
g_hal_usb.current_config = 0;
g_hal_usb.current_alt_interface = 0;
// Setup callbacks
g_hal_usb.device_req = device_req;
g_hal_usb.reset = reset;
g_hal_usb.resume = resume;
g_hal_usb.suspend = suspend;
// Disconnect from USB-bus if we are in this routine from a power on and not a soft reset
if(usb_disconnect)
{
i_usb.map->usbcs |= 0x08; // disconnect
delay_ms(50);
i_usb.map->usbcs &= ~(0x08); // connect
}
// Setup interrupts
USBWU = 1; // USBWU is mapped to IEN1.3
USB = 1; // USBIRQ is mapped to IEN1.4
i_usb.map->usbien = 0x1d; // ibnie -5 4 - uresir 3 - suspir, 0 - sudavir
i_usb.map->in_ien = 0x01;
i_usb.map->in_irq = 0x1f;
i_usb.map->out_ien = 0x01;
i_usb.map->out_irq = 0x1f;
// Setup the USB RAM with some OK default values. Note that isochronos is not set up yet.
i_usb.map->bout1addr = 16;
i_usb.map->bout2addr = 32;
i_usb.map->bout3addr = 48;
i_usb.map->bout4addr = 64;
i_usb.map->bout5addr = 80;
i_usb.map->binstaddr = 0xc0;
i_usb.map->bin1addr = 16;
i_usb.map->bin2addr = 32;
i_usb.map->bin3addr = 48;
i_usb.map->bin4addr = 64;
i_usb.map->bin5addr = 80;
// Set all endpoints to not valid (except EP0IN and EP0OUT)
i_usb.map->inbulkval = 0x01;
i_usb.map->outbulkval = 0x01;
i_usb.map->inisoval = 0x00;
i_usb.map->outisoval = 0x00;
}
void hal_usb_endpoint_stall(uint8_t ep_num, bool stall)
{
uint8_t temp;
uint8_t* cs_ptr;
temp = ep_num & 0x7f;
// Calculate register address
if( ( ep_num & 0x80 ) == 0x80 ) // IN endpoints
{
// Calculate control and status register for IN endpoint
cs_ptr = (uint8_t*)(&(i_usb.map->in1cs) + 2 * ( temp - 1 ));
}
else // OUT endpoints
{
// Calculate control and status register for OUT endpoint
cs_ptr = (uint8_t*)(&(i_usb.map->out1cs) + 2 * ( temp - 1 ));
}
if( stall == true )
{
// Set the stall bit
*cs_ptr = 0x01;
}
else
{
// Clear the stall bit
*cs_ptr = 0x00;
}
}
uint8_t hal_usb_get_address()
{
return i_usb.map->fnaddr;
}
void hal_usb_endpoint_config(uint8_t ep_num, uint8_t ep_size, hal_usb_cb_endpoint_t endpoint_isr)
{
/// @todo Create a function that setup the usbram correctly
xdata uint8_t *bc_ptr;
uint8_t temp = ep_num & 0x7f;
// Dummy use of variable to get rid of warning
ep_size = 0;
if( ( ep_num & 0x80 ) == 0x80 ) // MSB set indicates IN endpoint
{
if( endpoint_isr != NULL )
{
// Add the callback, enable the interrupt and validate the endpoint
i_usb.endpoint_in_isr[temp - 1] = endpoint_isr;
i_usb.map->in_ien |= ( 1 << temp );
i_usb.map->inbulkval |= (1 << temp );
}
else
{
//lint --e{502}
// Remove the callback, disable the interrupt and invalidate the endpoint
i_usb.endpoint_in_isr[temp - 1] = NULL;
i_usb.map->in_ien &= ~( 1 << temp );
i_usb.map->inbulkval &= ~(1 << temp );
}
}
else // OUT endpoint
{
if( endpoint_isr != NULL )
{
// Add the callback, enable the interrupt and validate the endpoint
i_usb.endpoint_out_isr[temp - 1] = endpoint_isr;
i_usb.map->out_ien |= ( 1 << temp );
i_usb.map->outbulkval |= (1 << temp );
// Have to write a dummy value to the OUTxBC register to get interrupts
bc_ptr = CALCULATE_BC_OUT_PTR(ep_num);
*bc_ptr = 0xff;
}
else
{
//lint --e{502}
// Remove the callback, disable the interrupt and invalidate the endpoint
i_usb.endpoint_out_isr[temp - 1] = NULL;
i_usb.map->out_ien &= ~( 1 << temp );
i_usb.map->outbulkval &= ~(1 << temp );
}
}
}
void hal_usb_wakeup()
{
// We can only issue a wakeup if the host has allowed us to do so
if( ( g_hal_usb.bm_state & USB_BM_STATE_ALLOW_REMOTE_WAKEUP ) == USB_BM_STATE_ALLOW_REMOTE_WAKEUP )
{
USBCON = 0x40; // Wakeup the USB controller via remote pin
delay_ms(1); // Wait until the USB clock starts
USBCON = 0x00;
}
}
void hal_usb_reset()
{
SWRST = 1; // Perform a hardware reset of the USB controller
}
hal_usb_state_t hal_usb_get_state()
{
return g_hal_usb.state;
}
void hal_usb_send_data(uint8_t ep_num, uint8_t* array, uint8_t count)
{
uint8_t i;
xdata uint8_t *buf_ptr;
xdata uint8_t *bc_ptr;
// Calculate the buffer pointer and byte count pointer
buf_ptr = CALCULATE_BUF_IN_PTR(ep_num);
bc_ptr = CALCULATE_BC_IN_PTR(ep_num);
// Copy the data into the USB controller
for( i = 0; i < count; i++ )
{
buf_ptr[i] = array[i];
}
// Set the number of bytes we want to send to USB-host. This also trigger sending of data to USB-host.
*bc_ptr = count;
}
void hal_usb_bus_disconnect()
{
i_usb.map->usbcs |= 0x08; // disconnect
}
void hal_usb_bus_connect()
{
i_usb.map->usbcs &= ~(0x08); // connect
}
void hal_usb_sleep()
{
USBSLP=1;
}
static void packetize(uint8_t* data_ptr, uint8_t data_size, uint8_t pkt_size)
{
i_usb.packetizer.data_ptr = data_ptr;
i_usb.packetizer.data_size = data_size;
i_usb.packetizer.pkt_size = pkt_size;
}
// This routine is called by functions that shall send their first packet and when the EP0IN interrupt is set
static void packetizer_isr_ep0_in()
{
uint16_t size, i;
// We are getting a ep0in interupt when the host send ACK and do not have any more data to send
if( i_usb.packetizer.data_size == 0 )
{
i_usb.map->in0bc = 0;
USB_EP0_HSNAK();
//USB_EP0_DSTALL();
return;
}
size = MIN(i_usb.packetizer.data_size, i_usb.packetizer.pkt_size);
// Copy data to the USB-controller buffer
for( i = 0; i < size; i++ )
{
i_usb.map->in0buf[i] = i_usb.packetizer.data_ptr[i];
}
// 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
i_usb.map->in0bc = size;
// Update the packetizer data
i_usb.packetizer.data_ptr += size;
i_usb.packetizer.data_size -= size;
return;
}
/** This function processes the response from the callback */
static void usb_process_dev_req_cb_response(hal_usb_dev_req_resp_t ret, hal_usb_device_req* req, uint8_t* data_ptr, uint16_t size)
{
switch( ret )
{
case STALL:
USB_EP0_STALL();
break;
case DATA:
packetize(data_ptr, MIN(LSB(req->wLength), size), g_hal_usb.descs.dev->bMaxPacketSize0);
packetizer_isr_ep0_in();
break;
case NO_RESPONSE:
break;
case EMPTY_RESPONSE:
USB_EP0_HSNAK();
break;
case NAK:
USB_EP0_HSNAK();
break;
case ACK:
i_usb.map->out0bc = 0xff;
break;
default:
USB_EP0_STALL();
break;
}
}
static void usb_process_get_status(hal_usb_device_req* req)
{
uint8_t* ptr;
if( g_hal_usb.state == ADDRESSED )
{
if( LSB(req->wIndex) != 0x00 )
{
USB_EP0_STALL();
}
else
{
i_usb.map->in0buf[0] = i_usb.map->in0buf[1] =
((g_hal_usb.descs.conf->conf.bmAttributes & 0x40 ) >> 6); // D0 - 0: bus powered, 1: self powered
i_usb.map->in0bc = 0x02;
}
}
else if( g_hal_usb.state == CONFIGURED )
{
switch(req->bmRequestType)
{
case 0x80: // Device
if( ( g_hal_usb.bm_state & USB_BM_STATE_ALLOW_REMOTE_WAKEUP ) == USB_BM_STATE_ALLOW_REMOTE_WAKEUP )
{
i_usb.map->in0buf[0] = 0x02;
}
else
{
i_usb.map->in0buf[0] = 0x00;
}
i_usb.map->in0buf[0] |= ((g_hal_usb.descs.conf->conf.bmAttributes & 0x40 ) >> 6); // D0 - 0: bus powered, 1: self powered
i_usb.map->in0buf[1] = 0x00; // Reserved (Reset to zero)
i_usb.map->in0bc = 0x02;
break;
case 0x81: // Interface
i_usb.map->in0buf[0] = i_usb.map->in0buf[1] = 0x00;
i_usb.map->in0bc = 0x02;
break;
case 0x82: // Endpoint
if( ( LSB(req->wIndex) & 0x80 ) == 0x80 ) // IN endpoints
{
ptr = CALCULATE_CS_IN_PTR(req->wIndex);
}
else
{
ptr = CALCULATE_CS_OUT_PTR(req->wIndex);
}
i_usb.map->in0buf[0] = *ptr & 0x01;
i_usb.map->in0buf[1] = 0x00;
i_usb.map->in0bc = 0x02;
break;
default:
USB_EP0_STALL();
break;
} // switch(req.bmRequestType) --end--
}
else
{
// We should not be in this state
USB_EP0_STALL();
}
}
static void usb_process_get_descriptor(hal_usb_device_req* req)
{
hal_usb_dev_req_resp_t ret;
uint8_t* data_ptr;
uint16_t data_size;
// Switch on descriptor type
switch( MSB(req->wValue) )
{
case USB_DESC_DEVICE:
packetize((uint8_t*)g_hal_usb.descs.dev,
MIN(req->wLength, sizeof(hal_usb_dev_desc_t)),
g_hal_usb.descs.dev->bMaxPacketSize0);
packetizer_isr_ep0_in();
break;
case USB_DESC_CONFIGURATION:
// For now we just support one configuration. The asked configuration is stored in LSB(wValue).
packetize((uint8_t*)g_hal_usb.descs.conf,
MIN(LSB(req->wLength), sizeof(usb_conf_desc_templ_t)),
g_hal_usb.descs.dev->bMaxPacketSize0);
packetizer_isr_ep0_in();
break;
case USB_DESC_STRING:
// For now we just support english as string descriptor language.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -