add code for SSC DMA and USB fast audio source

This version is able to acutually DMA data from SSC via USB audio into
the PC.
This commit is contained in:
Harald Welte 2012-02-28 03:11:29 +01:00
parent 20656119c0
commit e81bf0ed6f
11 changed files with 2865 additions and 257 deletions

View File

@ -0,0 +1,32 @@
#include <stdint.h>
#include "req_ctx.h"
/* Some thoughts:
* If we're running at 1MS/s, and the USB-HS microframe occurs every 125uS, we
* need to transfer 125 samples at eech microframe. 125 samples with 2
* channels of 2 bytes equals 500 bytes.
*/
#define AUDDLoopRecDriver_SAMPLERATE 500000
#define AUDDLoopRecDriver_NUMCHANNELS 2
#define AUDDLoopRecDriver_BYTESPERSAMPLE 2
#if 0
#define AUDDLoopRecDriver_SAMPLESPERFRAME (AUDDLoopRecDriver_SAMPLERATE / 8000 \
* AUDDLoopRecDriver_NUMCHANNELS)
#else
#define AUDDLoopRecDriver_SAMPLESPERFRAME (128 * AUDDLoopRecDriver_NUMCHANNELS)
#endif
#define AUDDLoopRecDriver_BYTESPERFRAME (AUDDLoopRecDriver_SAMPLESPERFRAME * \
AUDDLoopRecDriver_BYTESPERSAMPLE)
#include <usb/common/core/USBGenericRequest.h>
void fastsource_init(void);
void fastsource_start(void);
void usb_submit_req_ctx(struct req_ctx *rctx);

View File

@ -0,0 +1,149 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/**
\unit
!!!Purpose
Declaration of the descriptors required by a USB audio speaker driver.
!!!Usage
-# Initialize a USBDDriver instance using the
auddSpeakerDriverDescriptors list.
*/
#ifndef AUDDLOOPRECDESCRIPTORS_H
#define AUDDLOOPRECDESCRIPTORS_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
#include <fast_source.h>
#include <usb/device/core/USBDDriverDescriptors.h>
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "Audio Speaker Endpoint Numbers"
///
/// This page lists the endpoint number settings for USB Audio Speaker device.
///
/// !Endpoints
/// - AUDDLoopRecDriverDescriptors_DATAOUT
/// - AUDDLoopRecDriverDescriptors_DATAIN
/// - AUDDLoopRecDriverDescriptors_HS_INTERVAL
/// - AUDDLoopRecDriverDescriptors_FS_INTERVAL
#if defined(at91sam7s) || defined(at91sam9xe)
/// Data out endpoint number, size 64B.
#define AUDDLoopRecDriverDescriptors_DATAOUT 0x01
/// Data in endpoint number, size 64B
#define AUDDLoopRecDriverDescriptors_DATAIN 0x02
#elif defined(CHIP_USB_UDP)
/// Data out endpoint number, size 192B.
#define AUDDLoopRecDriverDescriptors_DATAOUT 0x04
/// Data in endpoint number, size 192B.
#define AUDDLoopRecDriverDescriptors_DATAIN 0x05
#elif defined(at91sam9m10ek) || defined(at91sam9m10ekes)
/// Data out endpoint number, size 192B.
#define AUDDLoopRecDriverDescriptors_DATAOUT 0x01
/// Data in endpoint number, size 192B
#define AUDDLoopRecDriverDescriptors_DATAIN 0x06
#else
/// Data out endpoint number, size 192B.
#define AUDDLoopRecDriverDescriptors_DATAOUT 0x05
/// Data in endpoint number, size 192B
#define AUDDLoopRecDriverDescriptors_DATAIN 0x06
#endif
/// Endpoint polling interval 2^(x-1) * 125us
#define AUDDLoopRecDriverDescriptors_HS_INTERVAL 0x01
/// Endpoint polling interval 2^(x-1) * ms
#define AUDDLoopRecDriverDescriptors_FS_INTERVAL 0x01
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "Audio Speaker Interface IDs"
///
/// This page lists the interface numbers for USB Audio Speaker device.
///
/// !Interfaces
/// - AUDDLoopRecDriverDescriptors_CONTROL
/// - AUDDLoopRecDriverDescriptors_STREAMING
/// - AUDDLoopRecDriverDescriptors_STREAMINGIN
/// Audio control interface ID.
#define AUDDLoopRecDriverDescriptors_CONTROL 0
/// Audio streaming interface ID (OUT, for playback).
#define AUDDLoopRecDriverDescriptors_STREAMING 1
/// Audio streaming interface ID (IN, for record).
#define AUDDLoopRecDriverDescriptors_STREAMINGIN 2
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// \page "Audio Speaker Entity IDs"
///
/// This page lists the entity IDs for USB Audio Speaker device.
///
/// !Entities
/// - AUDDLoopRecDriverDescriptors_INPUTTERMINAL
/// - AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL
/// - AUDDLoopRecDriverDescriptors_FEATUREUNIT
/// - AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC
/// - AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC
/// - AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC
/// Playback input terminal ID.
#define AUDDLoopRecDriverDescriptors_INPUTTERMINAL 0
/// Playback output terminal ID.
#define AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL 1
/// Playback feature unit ID.
#define AUDDLoopRecDriverDescriptors_FEATUREUNIT 2
/// Record input terminal ID.
#define AUDDLoopRecDriverDescriptors_INPUTTERMINAL_REC 3
/// Record output terminal ID.
#define AUDDLoopRecDriverDescriptors_OUTPUTTERMINAL_REC 4
/// Record feature unit ID
#define AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC 5
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Exported variables
//------------------------------------------------------------------------------
extern const USBDDriverDescriptors auddLoopRecDriverDescriptors;
#endif //#ifndef AUDDLOOPRECDESCRIPTORS_H

View File

@ -0,0 +1,360 @@
#ifndef _LINUX_LLIST_H
#define _LINUX_LLIST_H
#include <stddef.h>
#ifndef inline
#define inline __inline__
#endif
static inline void prefetch(const void *x) {;}
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \
(type *)( (char *)__mptr - offsetof(type, member) );})
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized llist entries.
*/
#define LLIST_POISON1 ((void *) 0x00100100)
#define LLIST_POISON2 ((void *) 0x00200200)
/*
* Simple doubly linked llist implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole llists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct llist_head {
struct llist_head *next, *prev;
};
#define LLIST_HEAD_INIT(name) { &(name), &(name) }
#define LLIST_HEAD(name) \
struct llist_head name = LLIST_HEAD_INIT(name)
#define INIT_LLIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal llist manipulation where we know
* the prev/next entries already!
*/
static inline void __llist_add(struct llist_head *_new,
struct llist_head *prev,
struct llist_head *next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
/**
* llist_add - add a new entry
* @new: new entry to be added
* @head: llist head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void llist_add(struct llist_head *_new, struct llist_head *head)
{
__llist_add(_new, head, head->next);
}
/**
* llist_add_tail - add a new entry
* @new: new entry to be added
* @head: llist head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
{
__llist_add(_new, head->prev, head);
}
/*
* Delete a llist entry by making the prev/next entries
* point to each other.
*
* This is only for internal llist manipulation where we know
* the prev/next entries already!
*/
static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* llist_del - deletes entry from llist.
* @entry: the element to delete from the llist.
* Note: llist_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void llist_del(struct llist_head *entry)
{
__llist_del(entry->prev, entry->next);
entry->next = (struct llist_head *)LLIST_POISON1;
entry->prev = (struct llist_head *)LLIST_POISON2;
}
/**
* llist_del_init - deletes entry from llist and reinitialize it.
* @entry: the element to delete from the llist.
*/
static inline void llist_del_init(struct llist_head *entry)
{
__llist_del(entry->prev, entry->next);
INIT_LLIST_HEAD(entry);
}
/**
* llist_move - delete from one llist and add as another's head
* @llist: the entry to move
* @head: the head that will precede our entry
*/
static inline void llist_move(struct llist_head *llist, struct llist_head *head)
{
__llist_del(llist->prev, llist->next);
llist_add(llist, head);
}
/**
* llist_move_tail - delete from one llist and add as another's tail
* @llist: the entry to move
* @head: the head that will follow our entry
*/
static inline void llist_move_tail(struct llist_head *llist,
struct llist_head *head)
{
__llist_del(llist->prev, llist->next);
llist_add_tail(llist, head);
}
/**
* llist_empty - tests whether a llist is empty
* @head: the llist to test.
*/
static inline int llist_empty(const struct llist_head *head)
{
return head->next == head;
}
static inline void __llist_splice(struct llist_head *llist,
struct llist_head *head)
{
struct llist_head *first = llist->next;
struct llist_head *last = llist->prev;
struct llist_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
/**
* llist_splice - join two llists
* @llist: the new llist to add.
* @head: the place to add it in the first llist.
*/
static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
{
if (!llist_empty(llist))
__llist_splice(llist, head);
}
/**
* llist_splice_init - join two llists and reinitialise the emptied llist.
* @llist: the new llist to add.
* @head: the place to add it in the first llist.
*
* The llist at @llist is reinitialised
*/
static inline void llist_splice_init(struct llist_head *llist,
struct llist_head *head)
{
if (!llist_empty(llist)) {
__llist_splice(llist, head);
INIT_LLIST_HEAD(llist);
}
}
/**
* llist_entry - get the struct for this entry
* @ptr: the &struct llist_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the llist_struct within the struct.
*/
#define llist_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* llist_for_each - iterate over a llist
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, prefetch(pos->next))
/**
* __llist_for_each - iterate over a llist
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*
* This variant differs from llist_for_each() in that it's the
* simplest possible llist iteration code, no prefetching is done.
* Use this for code that knows the llist to be very short (empty
* or 1 entry) most of the time.
*/
#define __llist_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* llist_for_each_prev - iterate over a llist backwards
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each_prev(pos, head) \
for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
pos = pos->prev, prefetch(pos->prev))
/**
* llist_for_each_safe - iterate over a llist safe against removal of llist entry
* @pos: the &struct llist_head to use as a loop counter.
* @n: another &struct llist_head to use as temporary storage
* @head: the head for your llist.
*/
#define llist_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* llist_for_each_entry - iterate over llist of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry(pos, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
/**
* llist_for_each_entry_reverse - iterate backwards over llist of given type.
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_reverse(pos, head, member) \
for (pos = llist_entry((head)->prev, typeof(*pos), member), \
prefetch(pos->member.prev); \
&pos->member != (head); \
pos = llist_entry(pos->member.prev, typeof(*pos), member), \
prefetch(pos->member.prev))
/**
* llist_for_each_entry_continue - iterate over llist of given type
* continuing after existing point
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_continue(pos, head, member) \
for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
/**
* llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_safe(pos, n, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
n = llist_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = llist_entry(n->member.next, typeof(*n), member))
/**
* llist_for_each_rcu - iterate over an rcu-protected llist
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each_rcu(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
#define __llist_for_each_rcu(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
/**
* llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
* against removal of llist entry
* @pos: the &struct llist_head to use as a loop counter.
* @n: another &struct llist_head to use as temporary storage
* @head: the head for your llist.
*/
#define llist_for_each_safe_rcu(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
/**
* llist_for_each_entry_rcu - iterate over rcu llist of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your llist.
* @member: the name of the llist_struct within the struct.
*/
#define llist_for_each_entry_rcu(pos, head, member) \
for (pos = llist_entry((head)->next, typeof(*pos), member), \
prefetch(pos->member.next); \
&pos->member != (head); \
pos = llist_entry(pos->member.next, typeof(*pos), member), \
({ smp_read_barrier_depends(); 0;}), \
prefetch(pos->member.next))
/**
* llist_for_each_continue_rcu - iterate over an rcu-protected llist
* continuing after existing point.
* @pos: the &struct llist_head to use as a loop counter.
* @head: the head for your llist.
*/
#define llist_for_each_continue_rcu(pos, head) \
for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
#endif

View File

@ -0,0 +1,11 @@
#ifndef _OSDR_SSC_H
#define _OSDR_SSC_H
#include <stdint.h>
int ssc_init(void);
int ssc_dma_start(void);
int ssc_dma_stop(void);
#endif

View File

@ -0,0 +1,53 @@
#ifndef _REQ_CTX_H
#define _REQ_CTX_H
#define __ramfunc
#define RCTX_SIZE_LARGE 2048
#define RCTX_SIZE_SMALL 1024
#define MAX_HDRSIZE sizeof(struct openpcd_hdr)
#define MAX_REQSIZE (64-MAX_HDRSIZE)
#define req_buf_payload(x) (x->data[x->hdr_len])
#define req_buf_hdr(x) (x->data[0])
#include <stdint.h>
#include <dmad/dmad.h>
#include <linuxlist.h>
struct req_ctx {
struct llist_head list;
volatile uint32_t state;
uint16_t size;
uint16_t tot_len;
DmaLinkList dma_lli;
uint8_t *data;
};
#define RCTX_STATE_FREE 0xfe
#define RCTX_STATE_UDP_RCV_BUSY 0x01
#define RCTX_STATE_UDP_RCV_DONE 0x02
#define RCTX_STATE_MAIN_PROCESSING 0x03
#define RCTX_STATE_UDP_EP2_PENDING 0x10
#define RCTX_STATE_UDP_EP2_BUSY 0x11
#define RCTX_STATE_UDP_EP3_PENDING 0x12
#define RCTX_STATE_UDP_EP3_BUSY 0x13
#define RCTX_STATE_SSC_RX_BUSY 0x20 /* currently being processed by Rx DMA */
#define RCTX_STATE_SSC_RX_PENDING 0x21 /* waiting in queue for Rx SSC DMA */
#define RCTX_STATE_INVALID 0xff
extern struct req_ctx __ramfunc *req_ctx_find_get(int large, unsigned long old_state, unsigned long new_state);
extern struct req_ctx *req_ctx_find_busy(void);
extern void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state);
extern void req_ctx_put(struct req_ctx *ctx);
extern uint8_t req_ctx_num(struct req_ctx *ctx);
void req_ctx_enqueue(struct llist_head *list, struct req_ctx *rctx);
struct req_ctx *req_ctx_dequeue(struct llist_head *list);
#endif /* _REQ_CTX_H */

View File

@ -45,7 +45,7 @@ BOARD = osmo-sdr
# TRACE_LEVEL_ERROR 2
# TRACE_LEVEL_FATAL 1
# TRACE_LEVEL_NO_TRACE 0
TRACE_LEVEL = 5
TRACE_LEVEL = 3
# Optimization level, put in comment for debugging
#OPTIMIZATION = -Os
@ -116,8 +116,7 @@ BOARDS = $(AT91LIB)/boards
UTILITY = $(AT91LIB)/utility
COMP = $(AT91LIB)/components
DRIVER = $(AT91LIB)/drivers
MEM = $(AT91LIB)/memories
MEM = $(EXT_LIBS)/memories
USB = $(AT91LIB)/usb
VPATH += $(UTILITY)
VPATH += $(PERIPH)/dbgu
@ -127,25 +126,29 @@ VPATH += $(PERIPH)/ssc
VPATH += $(PERIPH)/spi
VPATH += $(PERIPH)/twi
VPATH += $(PERIPH)/pmc
VPATH += $(PERIPH)/rstc
VPATH += $(PERIPH)/cp15
VPATH += $(BOARDS)/$(BOARD)
VPATH += $(BOARDS)/$(BOARD)/$(CHIP)
#VPATH += $(PERIPH)/mci
VPATH += $(DRIVER)/twi
#VPATH += $(MEM)/sdmmc
VPATH += $(PERIPH)/dma
VPATH += $(DRIVER)/dmad
VPATH += $(FATFS)/src
VPATH += $(MEM)
VPATH += $(AT91LIB)/cmsis
VPATH += ../src
VPATH += $(USB)/device/core
VPATH += $(USB)/device/dfu
VPATH += $(USB)/common/core
VPATH += $(USB)/common/audio
# Objects built from C source files
C_OBJECTS += main.o
C_OBJECTS += stdio.o
C_OBJECTS += math.o
C_OBJECTS += dbgu.o
C_OBJECTS += pio.o
C_OBJECTS += pio.o pio_it.o
C_OBJECTS += spi.o
C_OBJECTS += ssc.o
C_OBJECTS += twi.o
@ -154,13 +157,38 @@ C_OBJECTS += twid.o
C_OBJECTS += board_lowlevel.o
C_OBJECTS += trace.o
C_OBJECTS += led.o
C_OBJECTS += rstc.o
C_OBJECTS += board_memories.o
C_OBJECTS += USBD_UDPHS.o
C_OBJECTS += USBDDriver.o
C_OBJECTS += USBDCallbacks_Initialized.o
C_OBJECTS += USBDCallbacks_Reset.o
#C_OBJECTS += USBDCallbacks_Resumed.o
#C_OBJECTS += USBDCallbacks_Suspended.o
C_OBJECTS += USBDDriverCb_CfgChanged.o
#C_OBJECTS += USBDDriverCb_IfSettingChanged.o
C_OBJECTS += USBInterfaceRequest.o
C_OBJECTS += USBFeatureRequest.o
C_OBJECTS += USBGenericRequest.o
C_OBJECTS += USBGetDescriptorRequest.o
C_OBJECTS += USBSetAddressRequest.o
C_OBJECTS += USBSetConfigurationRequest.o
C_OBJECTS += USBGenericDescriptor.o
C_OBJECTS += USBConfigurationDescriptor.o
C_OBJECTS += USBEndpointDescriptor.o
C_OBJECTS += dfu_runtime.o
C_OBJECTS += dfu_driver.o
C_OBJECTS += AUDGenericRequest.o AUDFeatureUnitRequest.o
C_OBJECTS += fast_source_descr.o fast_source.o
C_OBJECTS += logging.o
C_OBJECTS += tuner_e4k.o
C_OBJECTS += tuner_e4k_transport.o
C_OBJECTS += si570.o
C_OBJECTS += osdr_fpga.o
C_OBJECTS += req_ctx.o osdr_ssc.o
# Objects for different chips

View File

@ -48,6 +48,7 @@
#include <tuner_e4k.h>
#include <si570.h>
#include <osdr_fpga.h>
#include <req_ctx.h>
#define SSC_MCK 49152000
@ -69,6 +70,13 @@
//------------------------------------------------------------------------------
// Local variables
//------------------------------------------------------------------------------
/// Use for power management
#define STATE_IDLE 0
/// The USB device is in suspend state
#define STATE_SUSPEND 4
/// The USB device is in resume state
#define STATE_RESUME 5
unsigned char USBState = STATE_IDLE;
/// List of pins to configure.
static const Pin pins[] = {PINS_TWI0, PIN_PCK0, PINS_LEDS, PINS_SPI0,
@ -97,188 +105,76 @@ static void power_peripherals(int on)
}
}
struct reg {
unsigned int offset;
const char *name;
};
/*----------------------------------------------------------------------------
* VBus monitoring (optional)
*----------------------------------------------------------------------------*/
struct reg dma_regs[] = {
{ 0, "GCFG" },
{ 4, "EN" },
{ 8, "SREQ" },
{ 0xC, "CREQ" },
{ 0x10, "LAST" },
{ 0x20, "EBCIMR" },
{ 0x24, "EBCISR" },
{ 0x30, "CHSR" },
{ 0, NULL}
};
/** VBus pin instance. */
static const Pin pinVbus = PIN_USB_VBUS;
struct reg dma_ch_regs[] = {
{ 0, "SADDR" },
{ 4, "DADDR" },
{ 8, "DSCR" },
{ 0xC, "CTRLA" },
{ 0x10, "CTRLB" },
{ 0x14, "CFG" },
{ 0, NULL}
};
void reg_dump(struct reg *regs, uint32_t *base)
/**
* Handles interrupts coming from PIO controllers.
*/
static void ISR_Vbus(const Pin *pPin)
{
struct reg *r;
for (r = regs; r->offset || r->name; r++) {
uint32_t *addr = ((uint8_t *)base + r->offset);
printf("%s\t%08x:\t%08x\n\r", r->name, addr, *addr);
}
/* Check current level on VBus */
if (PIO_Get(&pinVbus))
{
TRACE_INFO("VBUS conn\n\r");
USBD_Connect();
}
else
{
TRACE_INFO("VBUS discon\n\r");
USBD_Disconnect();
}
}
static void dma_dump_regs(void)
/**
* Configures the VBus pin to trigger an interrupt when the level on that pin
* changes.
*/
static void VBus_Configure( void )
{
reg_dump(dma_regs, AT91C_BASE_HDMA);
reg_dump(dma_ch_regs, (uint8_t *)AT91C_BASE_HDMA_CH_0 + (BOARD_SSC_DMA_CHANNEL*0x28));
/* Configure PIO */
PIO_Configure(&pinVbus, 1);
PIO_ConfigureIt(&pinVbus, ISR_Vbus);
PIO_EnableIt(&pinVbus);
/* Check current level on VBus */
if (PIO_Get(&pinVbus))
{
/* if VBUS present, force the connect */
USBD_Connect();
}
else
{
TRACE_INFO("discon\n\r");
USBD_Disconnect();
}
}
static void ssc_irq_hdlr(void)
{
AT91S_SSC *ssc = AT91C_BASE_SSC0;
uint8_t status = ssc->SSC_SR;
/*----------------------------------------------------------------------------
* Callbacks re-implementation
*----------------------------------------------------------------------------*/
//------------------------------------------------------------------------------
/// Invoked when the USB device leaves the Suspended state. By default,
/// configures the LEDs.
//------------------------------------------------------------------------------
void USBDCallbacks_Resumed(void)
{
USBState = STATE_RESUME;
}
static DmaLinkList LLI_CH[MAX_SSC_LLI_SIZE];
static int dma_complete = 0;
static const uint32_t dummy = 0xdeadbeef;
static void ssc_dma_single(void *dest, unsigned int len)
//------------------------------------------------------------------------------
/// Invoked when the USB device gets suspended. By default, turns off all LEDs.
//------------------------------------------------------------------------------
void USBDCallbacks_Suspended(void)
{
LED_Set(0);
dma_complete = 0;
memset(dest, 0, len);
/* clear any pending interrupts */
DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL);
DMA_GetStatus();
DMA_SetSourceBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_SINGLE,
(AT91C_HDMA_SRC_ADDRESS_MODE_FIXED >> 24));
DMA_SetSourceBufferSize(BOARD_SSC_DMA_CHANNEL,
#if 0
len>>2 | AT91C_HDMA_SCSIZE_4 | AT91C_HDMA_DCSIZE_4,
AT91C_HDMA_SRC_WIDTH_WORD >> 24,
AT91C_HDMA_DST_WIDTH_WORD >> 28, 0);
#else
len>>2 | AT91C_HDMA_SCSIZE_1 | AT91C_HDMA_DCSIZE_1,
AT91C_HDMA_SRC_WIDTH_BYTE >> 24,
AT91C_HDMA_DST_WIDTH_BYTE >> 28, 0);
#endif
DMA_SetDestBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_SINGLE,
(AT91C_HDMA_DST_ADDRESS_MODE_INCR >> 28));
DMA_SetFlowControl(BOARD_SSC_DMA_CHANNEL, AT91C_HDMA_FC_PER2MEM >> 21);
DMA_SetConfiguration(BOARD_SSC_DMA_CHANNEL, BOARD_SSC_DMA_HW_SRC_REQ_ID
| BOARD_SSC_DMA_HW_DEST_REQ_ID
| AT91C_HDMA_SRC_H2SEL_HW
| AT91C_HDMA_DST_H2SEL_HW
| AT91C_HDMA_SOD_DISABLE
| AT91C_HDMA_FIFOCFG_LARGESTBURST);
DMA_SetSourceAddr(BOARD_SSC_DMA_CHANNEL, &AT91C_BASE_SSC0->SSC_RHR);
//DMA_SetSourceAddr(BOARD_SSC_DMA_CHANNEL, &dummy);
DMA_SetDestinationAddr(BOARD_SSC_DMA_CHANNEL, (unsigned int) dest);
dma_dump_regs();
DMA_EnableChannel(BOARD_SSC_DMA_CHANNEL);
printf("Initialized Single DMA (len=%u)\n\r", len);
USBState = STATE_SUSPEND;
}
#define DMA_CTRLA (AT91C_HDMA_SRC_WIDTH_WORD|AT91C_HDMA_DST_WIDTH_WORD|AT91C_HDMA_SCSIZE_4|AT91C_HDMA_DCSIZE_4)
#define DMA_CTRLB (AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM | \
AT91C_HDMA_DST_ADDRESS_MODE_INCR | \
AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | \
AT91C_HDMA_SRC_ADDRESS_MODE_FIXED | \
AT91C_HDMA_FC_PER2MEM)
static void ssc_dma_llc(void *dest, unsigned int len)
{
LED_Set(0);
dma_complete = 0;
memset(dest, 0, len);
LLI_CH[0].sourceAddress = &AT91C_BASE_SSC0->SSC_RHR;
//LLI_CH[0].sourceAddress = &dummy;
LLI_CH[0].destAddress = (unsigned int) dest;
LLI_CH[0].controlA = len/4 | DMA_CTRLA;
LLI_CH[0].controlB = DMA_CTRLB;
LLI_CH[0].descriptor = 0;
/* clear any pending interrupts */
DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL);
DMA_GetStatus();
DMA_SetDescriptorAddr(BOARD_SSC_DMA_CHANNEL, (unsigned int)&LLI_CH[0]);
DMA_SetSourceAddr(BOARD_SSC_DMA_CHANNEL, &AT91C_BASE_SSC0->SSC_RHR);
DMA_SetSourceBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_LLI,
(AT91C_HDMA_SRC_ADDRESS_MODE_FIXED >> 24));
DMA_SetDestBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_LLI,
(AT91C_HDMA_DST_ADDRESS_MODE_INCR >> 28));
DMA_SetFlowControl(BOARD_SSC_DMA_CHANNEL, AT91C_HDMA_FC_PER2MEM >> 21);
DMA_SetConfiguration(BOARD_SSC_DMA_CHANNEL,
BOARD_SSC_DMA_HW_SRC_REQ_ID | BOARD_SSC_DMA_HW_DEST_REQ_ID
| AT91C_HDMA_SRC_H2SEL_HW \
| AT91C_HDMA_DST_H2SEL_SW \
| AT91C_HDMA_SOD_DISABLE \
| AT91C_HDMA_FIFOCFG_LARGESTBURST);
dma_dump_regs();
printf("enabling channel...\n\r");
DMA_EnableChannel(BOARD_SSC_DMA_CHANNEL);
printf("Initialized LLC DMA (len=%u)\n\r", len);
}
#define BTC(N) (1 << N)
#define CBTC(N) (1 << 8+N)
#define ERR(N) (1 << 16+N)
void HDMA_IrqHandler(void)
{
unsigned int status = DMA_GetStatus();
LED_Clear(0);
if (status & BTC(BOARD_SSC_DMA_CHANNEL)) {
dma_complete = 1;
LED_Clear(0);
}
}
static int ssc_init(void)
{
SSC_DisableReceiver(AT91C_BASE_SSC0);
SSC_Configure(AT91C_BASE_SSC0, AT91C_ID_SSC0, 0, BOARD_MCK);
SSC_ConfigureReceiver(AT91C_BASE_SSC0, AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE |
AT91C_SSC_CKG_NONE | AT91C_SSC_START_FALL_RF |
AT91C_SSC_CKI,
AT91C_SSC_MSBF | 32-1 );
#if 0
IRQ_ConfigureIT(AT91C_ID_SSC0, 0, ssc_irq_hdlr);
IRQ_EnableIT(AT91C_ID_SSC0);
//SSC_EnableInterrupts(AT91C_BASE_SSC0, AT91C_SSC_RXRDY | AT91C_SSC_OVRUN);
#endif
SSC_EnableReceiver(AT91C_BASE_SSC0);
/* Enable DMA controller and register interrupt handler */
PMC_EnablePeripheral(AT91C_ID_HDMA);
DMA_Enable();
IRQ_ConfigureIT(AT91C_ID_HDMA, 0, HDMA_IrqHandler);
IRQ_EnableIT(AT91C_ID_HDMA);
DMA_EnableIt(BTC(BOARD_SSC_DMA_CHANNEL) | CBTC(BOARD_SSC_DMA_CHANNEL) |
ERR(BOARD_SSC_DMA_CHANNEL));
printf("initialzied SSC\n\r");
}
static void DisplayMenu(void)
{
printf("Menu:\r\n"
@ -297,75 +193,10 @@ static void DisplayMenu(void)
);
}
static uint32_t dma_buf[1024/4];
static int freq = 800;
//------------------------------------------------------------------------------
/// Main function
//------------------------------------------------------------------------------
int main(void)
static void handle_input(unsigned char key)
{
unsigned char key;
unsigned char isValid;
static int freq = 800;
// Configure all pins
PIO_Configure(pins, PIO_LISTSIZE(pins));
LED_Configure(0);
LED_Set(0);
LED_Configure(1);
LED_Set(1);
// Initialize the DBGU
TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK);
printf("trace configured!!\n");
// Switch to Main clock
AT91C_BASE_PMC->PMC_MCKR = (AT91C_BASE_PMC->PMC_MCKR & ~AT91C_PMC_CSS) | AT91C_PMC_CSS_MAIN_CLK;
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0);
// Configure PLL to 98.285MHz
*AT91C_CKGR_PLLR = ((1 << 29) | (171 << AT91C_CKGR_MUL_SHIFT) \
| (0x0 << AT91C_CKGR_OUT_SHIFT) |(0x3f << AT91C_CKGR_PLLCOUNT_SHIFT) \
| (21 << AT91C_CKGR_DIV_SHIFT));
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK) == 0);
// Configure master clock in two operations
AT91C_BASE_PMC->PMC_MCKR = (( AT91C_PMC_PRES_CLK_2 | AT91C_PMC_CSS_PLLA_CLK) & ~AT91C_PMC_CSS) | AT91C_PMC_CSS_MAIN_CLK;
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0);
AT91C_BASE_PMC->PMC_MCKR = ( AT91C_PMC_PRES_CLK_2 | AT91C_PMC_CSS_PLLA_CLK);
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0);
// DBGU reconfiguration
DBGU_Configure(DBGU_STANDARD, 115200, SSC_MCK);
// Configure and enable the TWI (required for accessing the DAC)
*AT91C_PMC_PCER = (1<< AT91C_ID_TWI0);
TWI_ConfigureMaster(AT91C_BASE_TWI0, TWI_CLOCK, SSC_MCK);
TWID_Initialize(&twid, AT91C_BASE_TWI0);
printf("-- osmo-sdr testing project %s --\n\r", SOFTPACK_VERSION);
printf("-- %s\n\r", BOARD_NAME);
printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__);
power_peripherals(1);
si570_init(&si570, &twid, SI570_I2C_ADDR);
set_si570_freq(30000000);
osdr_fpga_init(SSC_MCK);
osdr_fpga_reg_write(OSDR_FPGA_REG_ADC_TIMING, (1 << 8) | 255);
osdr_fpga_reg_write(OSDR_FPGA_REG_PWM1, (1 << 400) | 800);
// Enter menu loop
while (1) {
// Display menu
DisplayMenu();
// Process user input
key = DBGU_GetChar();
//key = 0;
switch (key) {
case '0':
power_peripherals(1);
@ -416,27 +247,96 @@ int main(void)
ssc_init();
break;
case 'S':
SSC_DisableReceiver(AT91C_BASE_SSC0);
//ssc_dma_single(dma_buf, sizeof(dma_buf));
ssc_dma_llc(dma_buf, sizeof(dma_buf));
SSC_EnableReceiver(AT91C_BASE_SSC0);
ssc_dma_start();
break;
case 'F':
fastsource_start();
break;
}
//osdr_fpga_reg_write(OSDR_FPGA_REG_PWM1, ((freq/2) << 16) | freq);
printf("\t\t\tcur_ssc_data = 0x%08x\n\r", AT91C_BASE_SSC0->SSC_RHR);
{
int i;
for (i = 0; i < sizeof(dma_buf)/sizeof(dma_buf[0]); i++) {
if (i == 0 || dma_buf[i] != dma_buf[i-1])
printf("\t\t\tdma_ssc_data[%u] = 0x%08x\n\r", i, dma_buf[i]);
//break;
}
}
dma_dump_regs();
if (dma_complete) {
printf("=======> DMA complete\n\r");
dma_complete = 0;
}
//------------------------------------------------------------------------------
/// Main function
//------------------------------------------------------------------------------
int main(void)
{
unsigned char key;
unsigned char isValid;
// Configure all pins
PIO_Configure(pins, PIO_LISTSIZE(pins));
LED_Configure(0);
LED_Set(0);
LED_Configure(1);
LED_Set(1);
// Initialize the DBGU
TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK);
printf("trace configured!!\n");
// Switch to Main clock
AT91C_BASE_PMC->PMC_MCKR = (AT91C_BASE_PMC->PMC_MCKR & ~AT91C_PMC_CSS) | AT91C_PMC_CSS_MAIN_CLK;
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0);
// Configure PLL to 98.285MHz
*AT91C_CKGR_PLLR = ((1 << 29) | (171 << AT91C_CKGR_MUL_SHIFT) \
| (0x0 << AT91C_CKGR_OUT_SHIFT) |(0x3f << AT91C_CKGR_PLLCOUNT_SHIFT) \
| (21 << AT91C_CKGR_DIV_SHIFT));
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK) == 0);
// Configure master clock in two operations
AT91C_BASE_PMC->PMC_MCKR = (( AT91C_PMC_PRES_CLK_2 | AT91C_PMC_CSS_PLLA_CLK) & ~AT91C_PMC_CSS) | AT91C_PMC_CSS_MAIN_CLK;
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0);
AT91C_BASE_PMC->PMC_MCKR = ( AT91C_PMC_PRES_CLK_2 | AT91C_PMC_CSS_PLLA_CLK);
while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0);
// DBGU reconfiguration
DBGU_Configure(DBGU_STANDARD, 115200, SSC_MCK);
// Configure and enable the TWI (required for accessing the DAC)
*AT91C_PMC_PCER = (1<< AT91C_ID_TWI0);
TWI_ConfigureMaster(AT91C_BASE_TWI0, TWI_CLOCK, SSC_MCK);
TWID_Initialize(&twid, AT91C_BASE_TWI0);
printf("-- osmo-sdr testing project %s --\n\r", SOFTPACK_VERSION);
printf("-- %s\n\r", BOARD_NAME);
printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__);
req_ctx_init();
PIO_InitializeInterrupts(0);
fastsource_init();
VBus_Configure();
power_peripherals(1);
si570_init(&si570, &twid, SI570_I2C_ADDR);
set_si570_freq(30000000);
osdr_fpga_init(SSC_MCK);
osdr_fpga_reg_write(OSDR_FPGA_REG_ADC_TIMING, (1 << 8) | 255);
osdr_fpga_reg_write(OSDR_FPGA_REG_PWM1, (1 << 400) | 800);
// Enter menu loop
while (1) {
if (DBGU_IsRxReady()) {
key = DBGU_GetChar();
// Process user input
handle_input(key);
// Display menu
DisplayMenu();
ssc_stats();
}
}
}

218
firmware/src/fast_source.c Normal file
View File

@ -0,0 +1,218 @@
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <board.h>
#include <utility/trace.h>
#include <utility/led.h>
#include <usb/common/core/USBGenericRequest.h>
#include <usb/device/core/USBD.h>
#include <usb/device/core/USBDDriver.h>
#include <usb/device/core/USBDDriverDescriptors.h>
#include <usb/device/core/USBDCallbacks.h>
#include <usb/common/audio/AUDGenericRequest.h>
#include <usb/common/audio/AUDFeatureUnitRequest.h>
#include <fast_source_descr.h>
#include <fast_source.h>
extern const USBDDriverDescriptors auddFastSourceDriverDescriptors;
static unsigned char driver_interfaces[3];
static USBDDriver fast_source_driver;
struct usb_state {
struct llist_head queue;
int active;
};
static struct usb_state usb_state;
#define EP_NR 6
static void fastsource_get_feat_cur_val(uint8_t entity, uint8_t channel,
uint8_t control, uint8_t length)
{
/* FIXME */
USBD_Stall(0);
}
static void fastsource_set_feat_cur_val(uint8_t entity, uint8_t channel,
uint8_t control, uint8_t length)
{
/* FIXME */
USBD_Stall(0);
}
/* handler for EP0 (control) requests */
void fastsource_req_hdlr(const USBGenericRequest *request)
{
unsigned char entity;
unsigned char interface;
switch (USBGenericRequest_GetType(request)) {
case USBGenericRequest_STANDARD:
USBDDriver_RequestHandler(&fast_source_driver, request);
return;
case USBGenericRequest_CLASS:
/* continue below */
break;
default:
TRACE_WARNING("Unsupported request type %u\n\r",
USBGenericRequest_GetType(request));
USBD_Stall(0);
return;
}
switch (USBGenericRequest_GetRequest(request)) {
case AUDGenericRequest_SETCUR:
entity = AUDGenericRequest_GetEntity(request);
interface = AUDGenericRequest_GetInterface(request);
if (((entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) ||
(entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC)) &&
(interface == AUDDLoopRecDriverDescriptors_CONTROL)) {
fastsource_set_feat_cur_val(entity,
AUDFeatureUnitRequest_GetChannel(request),
AUDFeatureUnitRequest_GetControl(request),
USBGenericRequest_GetLength(request));
} else {
TRACE_WARNING("Unsupported entity/interface combination 0x%04x\n\r",
USBGenericRequest_GetIndex(request));
USBD_Stall(0);
}
break;
case AUDGenericRequest_GETCUR:
entity = AUDGenericRequest_GetEntity(request);
interface = AUDGenericRequest_GetInterface(request);
if (((entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT) ||
(entity == AUDDLoopRecDriverDescriptors_FEATUREUNIT_REC)) &&
(interface == AUDDLoopRecDriverDescriptors_CONTROL)) {
fastsource_get_feat_cur_val(entity,
AUDFeatureUnitRequest_GetChannel(request),
AUDFeatureUnitRequest_GetControl(request),
USBGenericRequest_GetLength(request));
} else {
TRACE_WARNING("Unsupported entity/interface combination 0x%04x\n\r",
USBGenericRequest_GetIndex(request));
USBD_Stall(0);
}
break;
default:
TRACE_WARNING("Unsupported request %u\n\r",
USBGenericRequest_GetIndex(request));
USBD_Stall(0);
break;
}
}
/* Initialize the driver */
void fastsource_init(void)
{
INIT_LLIST_HEAD(&usb_state.queue);
USBDDriver_Initialize(&fast_source_driver, &auddFastSourceDriverDescriptors,
driver_interfaces);
USBD_Init();
}
static int refill_dma(void);
/* completion callback: USBD_Write() has completed an IN transfer */
static void wr_compl_cb(void *arg, unsigned char status, unsigned int transferred,
unsigned int remain)
{
struct req_ctx *rctx = arg;
usb_state.active = 0;
req_ctx_set_state(rctx, RCTX_STATE_FREE);
if (status == 0 && remain == 0) {
refill_dma();
} else {
TRACE_WARNING("Err: EP%u wr_compl, status 0x%02u, xfr %u, remain %u\r\n",
EP_NR, status, transferred, remain);
}
}
static int refill_dma(void)
{
struct req_ctx *rctx;
rctx = req_ctx_dequeue(&usb_state.queue);
if (!rctx) {
//TRACE_WARNING("No rctx for re-filling USB DMA\n\r");
usb_state.active = 0;
return -ENOENT;
}
req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_BUSY);
if (USBD_Write(EP_NR, rctx->data, rctx->tot_len, wr_compl_cb,
rctx) != USBD_STATUS_SUCCESS) {
TRACE_WARNING("USB EP busy while re-filling USB DMA\n\r");
usb_state.active = 0;
return -EBUSY;
}
usb_state.active = 1;
return 0;
}
/* user API: requests us to start transmitting data via USB IN EP */
void fastsource_start(void)
{
if (!usb_state.active) {
usb_state.active = 1;
refill_dma();
}
}
/* SSC DMA informs us about completion of filling one rctx */
void usb_submit_req_ctx(struct req_ctx *rctx)
{
req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
//TRACE_INFO("USB rctx enqueue (%08x, %u/%u)\n\r", rctx, rctx->size, rctx->tot_len);
req_ctx_enqueue(&usb_state.queue, rctx);
fastsource_start();
}
/* callback */
void USBDCallbacks_RequestReceived(const USBGenericRequest *request)
{
fastsource_req_hdlr(request);
}
void USBDDriverCallbacks_InterfaceSettingChanged(unsigned char interface,
unsigned char setting)
{
if ((interface == AUDDLoopRecDriverDescriptors_STREAMING)
&& (setting == 0))
LED_Clear(USBD_LEDOTHER);
else
LED_Set(USBD_LEDOTHER);
}

File diff suppressed because it is too large Load Diff

264
firmware/src/osdr_ssc.c Normal file
View File

@ -0,0 +1,264 @@
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <board.h>
#include <errno.h>
#include <irq/irq.h>
#include <dbgu/dbgu.h>
#include <ssc/ssc.h>
#include <pmc/pmc.h>
#include <utility/assert.h>
#include <utility/math.h>
#include <utility/trace.h>
#include <utility/led.h>
//#include <dmad/dmad.h>
#include <dma/dma.h>
#include <req_ctx.h>
struct reg {
unsigned int offset;
const char *name;
};
void reg_dump(struct reg *regs, uint32_t *base)
{
struct reg *r;
for (r = regs; r->offset || r->name; r++) {
uint32_t *addr = (uint32_t *) ((uint8_t *)base + r->offset);
TRACE_INFO("%s\t%08x:\t%08x\n\r", r->name, addr, *addr);
}
}
#define DMA_CTRLA (AT91C_HDMA_SRC_WIDTH_WORD|AT91C_HDMA_DST_WIDTH_WORD|AT91C_HDMA_SCSIZE_1|AT91C_HDMA_DCSIZE_4)
#define DMA_CTRLB (AT91C_HDMA_DST_DSCR_FETCH_FROM_MEM | \
AT91C_HDMA_DST_ADDRESS_MODE_INCR | \
AT91C_HDMA_SRC_DSCR_FETCH_DISABLE | \
AT91C_HDMA_SRC_ADDRESS_MODE_FIXED | \
AT91C_HDMA_FC_PER2MEM)
struct reg dma_regs[] = {
{ 0, "GCFG" },
{ 4, "EN" },
{ 8, "SREQ" },
{ 0xC, "CREQ" },
{ 0x10, "LAST" },
{ 0x20, "EBCIMR" },
{ 0x24, "EBCISR" },
{ 0x30, "CHSR" },
{ 0, NULL}
};
struct reg dma_ch_regs[] = {
{ 0, "SADDR" },
{ 4, "DADDR" },
{ 8, "DSCR" },
{ 0xC, "CTRLA" },
{ 0x10, "CTRLB" },
{ 0x14, "CFG" },
{ 0, NULL}
};
static void dma_dump_regs(void)
{
reg_dump(dma_regs, (uint32_t *) AT91C_BASE_HDMA);
reg_dump(dma_ch_regs, (uint8_t *)AT91C_BASE_HDMA_CH_0 + (BOARD_SSC_DMA_CHANNEL*0x28));
}
struct ssc_state {
struct llist_head pending_rctx;
int hdma_chain_len;
int active;
uint32_t total_xfers;
uint32_t total_irqs;
};
struct ssc_state ssc_state;
#define INTENDED_HDMA_C_LEN 10
static void __refill_dma()
{
int missing = INTENDED_HDMA_C_LEN - ssc_state.hdma_chain_len;
int i;
for (i = 0; i < missing; i++) {
struct req_ctx *rctx;
/* obtain an unused request context from pool */
rctx = req_ctx_find_get(0, RCTX_STATE_FREE,
RCTX_STATE_SSC_RX_PENDING);
if (!rctx) {
break;
}
/* populate DMA descriptor inside request context */
rctx->dma_lli.sourceAddress = (unsigned int) &AT91C_BASE_SSC0->SSC_RHR;
rctx->dma_lli.destAddress = rctx->data;
rctx->dma_lli.controlA = DMA_CTRLA | (rctx->size/4);
rctx->dma_lli.controlB = DMA_CTRLB;
rctx->dma_lli.descriptor = 0; /* end of list */
/* append to list and update end pointer */
if (!llist_empty(&ssc_state.pending_rctx)) {
struct req_ctx *prev_rctx = llist_entry(ssc_state.pending_rctx.prev,
struct req_ctx, list);
prev_rctx->dma_lli.descriptor = &rctx->dma_lli;
}
req_ctx_enqueue(&ssc_state.pending_rctx, rctx);
ssc_state.hdma_chain_len++;
}
if (ssc_state.hdma_chain_len <= 1)
TRACE_ERROR("Unable to get rctx for SSC DMA refill\n\r");
}
int ssc_dma_start(void)
{
struct req_ctx *rctx;
__refill_dma();
if (ssc_state.active) {
TRACE_WARNING("Cannot start SSC DMA, active == 1\n\r");
return -EBUSY;
}
if (llist_empty(&ssc_state.pending_rctx)) {
TRACE_WARNING("Cannot start SSC DMA, no rctx pending\n\r");
return -ENOMEM;
}
rctx = llist_entry(ssc_state.pending_rctx.next, struct req_ctx, list);
/* clear any pending interrupts */
DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL);
DMA_GetStatus();
DMA_SetDescriptorAddr(BOARD_SSC_DMA_CHANNEL, &rctx->dma_lli);
DMA_SetSourceAddr(BOARD_SSC_DMA_CHANNEL, &AT91C_BASE_SSC0->SSC_RHR);
DMA_SetSourceBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_LLI,
(AT91C_HDMA_SRC_ADDRESS_MODE_FIXED >> 24));
DMA_SetDestBufferMode(BOARD_SSC_DMA_CHANNEL, DMA_TRANSFER_LLI,
(AT91C_HDMA_DST_ADDRESS_MODE_INCR >> 28));
DMA_SetFlowControl(BOARD_SSC_DMA_CHANNEL, AT91C_HDMA_FC_PER2MEM >> 21);
DMA_SetConfiguration(BOARD_SSC_DMA_CHANNEL,
BOARD_SSC_DMA_HW_SRC_REQ_ID | BOARD_SSC_DMA_HW_DEST_REQ_ID
| AT91C_HDMA_SRC_H2SEL_HW \
| AT91C_HDMA_DST_H2SEL_SW \
| AT91C_HDMA_SOD_DISABLE \
| AT91C_HDMA_FIFOCFG_LARGESTBURST);
dma_dump_regs();
ssc_state.active = 1;
DMA_EnableChannel(BOARD_SSC_DMA_CHANNEL);
LED_Set(0);
TRACE_INFO("Started SSC DMA\n\r");
SSC_EnableReceiver(AT91C_BASE_SSC0);
return 0;
}
int ssc_dma_stop(void)
{
SSC_DisableReceiver(AT91C_BASE_SSC0);
/* clear any pending interrupts */
DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL);
DMA_GetStatus();
return 0;
}
#define BTC(N) (1 << N)
#define CBTC(N) (1 << (8+N))
#define ERR(N) (1 << (16+N))
/* for some strange reason this cannot be static! */
void HDMA_IrqHandler(void)
{
unsigned int status = DMA_GetStatus();
struct req_ctx *rctx, *rctx2;
ssc_state.total_irqs++;
if (status & BTC(BOARD_SSC_DMA_CHANNEL)) {
llist_for_each_entry_safe(rctx, rctx2, &ssc_state.pending_rctx, list) {
if (!(rctx->dma_lli.controlA & AT91C_HDMA_DONE))
continue;
/* a single buffer has been completed */
ssc_state.total_xfers++;
llist_del(&rctx->list);
ssc_state.hdma_chain_len--;
rctx->tot_len = rctx->size;
#if 1
usb_submit_req_ctx(rctx);
#else
req_ctx_set_state(rctx, RCTX_STATE_FREE);
#endif
__refill_dma();
}
}
if (status & CBTC(BOARD_SSC_DMA_CHANNEL)) {
/* the end of the list was reached */
LED_Clear(0);
SSC_DisableReceiver(AT91C_BASE_SSC0);
TRACE_WARNING("SSC DMA buffer end reached, disabling after %u/%u\n\r",
ssc_state.total_irqs, ssc_state.total_xfers);
}
}
int ssc_init(void)
{
memset(&ssc_state, 0, sizeof(ssc_state));
INIT_LLIST_HEAD(&ssc_state.pending_rctx);
SSC_DisableReceiver(AT91C_BASE_SSC0);
SSC_Configure(AT91C_BASE_SSC0, AT91C_ID_SSC0, 0, BOARD_MCK);
SSC_ConfigureReceiver(AT91C_BASE_SSC0, AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE |
AT91C_SSC_CKG_NONE | AT91C_SSC_START_FALL_RF |
AT91C_SSC_CKI,
AT91C_SSC_MSBF | (32-1) );
/* Enable DMA controller and register interrupt handler */
PMC_EnablePeripheral(AT91C_ID_HDMA);
DMA_Enable();
IRQ_ConfigureIT(AT91C_ID_HDMA, 0, HDMA_IrqHandler);
IRQ_EnableIT(AT91C_ID_HDMA);
DMA_EnableIt(BTC(BOARD_SSC_DMA_CHANNEL) | CBTC(BOARD_SSC_DMA_CHANNEL) |
ERR(BOARD_SSC_DMA_CHANNEL));
TRACE_INFO("SSC initialized\n\r");
LED_Clear(0);
return 0;
}
void ssc_stats(void)
{
printf("SSC num_irq=%u, num_xfers=%u\n\r", ssc_state.total_irqs, ssc_state.total_xfers);
}

131
firmware/src/req_ctx.c Normal file
View File

@ -0,0 +1,131 @@
/* AT91SAM7 USB Request Context for OpenPCD / OpenPICC
* (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <board.h>
#define __INLINE inline
#define IRQn_Type int
#include <cmsis/core_cm3.h>
#include "req_ctx.h"
#define local_irq_save(x) __disable_fault_irq()
#define local_irq_restore(x) __enable_fault_irq()
#define NUM_RCTX_SMALL 16
#define NUM_RCTX_LARGE 2
#define NUM_REQ_CTX (NUM_RCTX_SMALL+NUM_RCTX_LARGE)
static uint8_t rctx_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL];
static uint8_t rctx_data_large[NUM_RCTX_LARGE][RCTX_SIZE_LARGE];
static struct req_ctx req_ctx[NUM_REQ_CTX];
struct req_ctx __ramfunc *req_ctx_find_get(int large,
unsigned long old_state,
unsigned long new_state)
{
unsigned long flags;
uint8_t i;
if (large)
i = NUM_RCTX_SMALL;
else
i = 0;
for (; i < NUM_REQ_CTX; i++) {
local_irq_save(flags);
if (req_ctx[i].state == old_state) {
req_ctx[i].state = new_state;
local_irq_restore(flags);
return &req_ctx[i];
}
local_irq_restore(flags);
}
return NULL;
}
uint8_t req_ctx_num(struct req_ctx *ctx)
{
return ((char *)ctx - (char *)&req_ctx[0])/sizeof(*ctx);
}
void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state)
{
unsigned long flags;
/* FIXME: do we need this kind of locking, we're UP! */
local_irq_save(flags);
ctx->state = new_state;
local_irq_restore(flags);
}
void req_ctx_put(struct req_ctx *ctx)
{
req_ctx_set_state(ctx, RCTX_STATE_FREE);
}
void req_ctx_init(void)
{
int i;
for (i = 0; i < NUM_RCTX_SMALL; i++) {
req_ctx[i].size = RCTX_SIZE_SMALL;
req_ctx[i].data = rctx_data[i];
req_ctx[i].state = RCTX_STATE_FREE;
}
for (i = 0; i < NUM_RCTX_LARGE; i++) {
req_ctx[NUM_RCTX_SMALL+i].size = RCTX_SIZE_LARGE;
req_ctx[NUM_RCTX_SMALL+i].data = rctx_data_large[i];
}
}
struct req_ctx *req_ctx_dequeue(struct llist_head *list)
{
unsigned long flags;
struct req_ctx *rctx;
local_irq_save(flags);
if (llist_empty(list)) {
local_irq_restore(flags);
return NULL;
}
rctx = llist_entry(list->next, struct req_ctx, list);
llist_del(&rctx->list);
local_irq_restore(flags);
return rctx;
}
void req_ctx_enqueue(struct llist_head *list, struct req_ctx *rctx)
{
unsigned long flags;
/* FIXME: do we need this kind of locking, we're UP! */
local_irq_save(flags);
llist_add_tail(&rctx->list, list);
local_irq_restore(flags);
}