improved USB serial Rx and implemented buffered Tx
This commit is contained in:
parent
082a78e3a6
commit
0ec7e72b01
|
@ -261,18 +261,28 @@ static ONE_DESCRIPTOR String_Descriptor[N_STRING_DESCRIPTORS] = {
|
||||||
|
|
||||||
/* I/O state */
|
/* I/O state */
|
||||||
|
|
||||||
#define CDC_SERIAL_BUFFER_SIZE 512
|
#define CDC_SERIAL_RX_BUFFER_SIZE 256 // must be power of 2
|
||||||
|
#define CDC_SERIAL_RX_BUFFER_SIZE_MASK (CDC_SERIAL_RX_BUFFER_SIZE-1)
|
||||||
|
|
||||||
/* Received data */
|
/* Received data */
|
||||||
static volatile uint8 vcomBufferRx[CDC_SERIAL_BUFFER_SIZE];
|
static volatile uint8 vcomBufferRx[CDC_SERIAL_RX_BUFFER_SIZE];
|
||||||
/* Read index into vcomBufferRx */
|
/* Write index to vcomBufferRx */
|
||||||
static volatile uint32 rx_offset = 0;
|
static volatile uint32 rx_head;
|
||||||
/* Number of bytes left to transmit */
|
/* Read index from vcomBufferRx */
|
||||||
static volatile uint32 n_unsent_bytes = 0;
|
static volatile uint32 rx_tail;
|
||||||
/* Are we currently sending an IN packet? */
|
|
||||||
static volatile uint8 transmitting = 0;
|
#define CDC_SERIAL_TX_BUFFER_SIZE 256 // must be power of 2
|
||||||
/* Number of unread bytes */
|
#define CDC_SERIAL_TX_BUFFER_SIZE_MASK (CDC_SERIAL_TX_BUFFER_SIZE-1)
|
||||||
static volatile uint32 n_unread_bytes = 0;
|
// Tx data
|
||||||
|
static volatile uint8 vcomBufferTx[CDC_SERIAL_TX_BUFFER_SIZE];
|
||||||
|
// Write index to vcomBufferTx
|
||||||
|
static volatile uint32 tx_head;
|
||||||
|
// Read index from vcomBufferTx
|
||||||
|
static volatile uint32 tx_tail;
|
||||||
|
// Are we currently sending an IN packet?
|
||||||
|
static volatile int8 transmitting;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Other state (line coding, DTR/RTS) */
|
/* Other state (line coding, DTR/RTS) */
|
||||||
|
|
||||||
|
@ -393,30 +403,34 @@ void usb_cdcacm_putc(char ch) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is blocking.
|
/* This function is non-blocking.
|
||||||
*
|
*
|
||||||
* It copies data from a usercode buffer into the USB peripheral TX
|
* It copies data from a user buffer into the USB peripheral TX
|
||||||
* buffer, and returns the number of bytes copied. */
|
* buffer, and returns the number of bytes copied. */
|
||||||
uint32 usb_cdcacm_tx(const uint8* buf, uint32 len) {
|
uint32 usb_cdcacm_tx(const uint8* buf, uint32 len)
|
||||||
/* Last transmission hasn't finished, so abort. */
|
{
|
||||||
while ( usb_cdcacm_is_transmitting()>0 ) ; // wait for end of transmission
|
if (len==0) return 0; // no data to send
|
||||||
|
|
||||||
/* We can only put USB_CDCACM_TX_EPSIZE bytes in the buffer. */
|
uint32 head = tx_head; // load volatile variable
|
||||||
if (len > USB_CDCACM_TX_EPSIZE) {
|
uint32 tx_unsent = (head - tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||||
len = USB_CDCACM_TX_EPSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Queue bytes for sending. */
|
// We can only put bytes in the buffer if there is place
|
||||||
if (len) {
|
if (len > (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1) ) {
|
||||||
usb_copy_to_pma(buf, len, USB_CDCACM_TX_ADDR);
|
len = (CDC_SERIAL_TX_BUFFER_SIZE-tx_unsent-1);
|
||||||
}
|
}
|
||||||
// We still need to wait for the interrupt, even if we're sending
|
if (len==0) return 0; // buffer full
|
||||||
// zero bytes. (Sending zero-size packets is useful for flushing
|
|
||||||
// host-side buffers.)
|
uint16 i;
|
||||||
usb_set_ep_tx_count(USB_CDCACM_TX_ENDP, len);
|
// copy data from user buffer to USB Tx buffer
|
||||||
n_unsent_bytes = len;
|
for (i=0; i<len; i++) {
|
||||||
transmitting = 1;
|
vcomBufferTx[head] = buf[i];
|
||||||
usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_VALID);
|
head = (head+1) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||||
|
}
|
||||||
|
tx_head = head; // store volatile variable
|
||||||
|
|
||||||
|
if (transmitting<0) {
|
||||||
|
vcomDataTxCb(); // initiate data transmission
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
@ -424,69 +438,73 @@ uint32 usb_cdcacm_tx(const uint8* buf, uint32 len) {
|
||||||
|
|
||||||
|
|
||||||
uint32 usb_cdcacm_data_available(void) {
|
uint32 usb_cdcacm_data_available(void) {
|
||||||
return n_unread_bytes;
|
return (rx_head - rx_tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 usb_cdcacm_is_transmitting(void) {
|
uint8 usb_cdcacm_is_transmitting(void) {
|
||||||
return transmitting;
|
return ( transmitting>0 ? transmitting : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 usb_cdcacm_get_pending(void) {
|
uint16 usb_cdcacm_get_pending(void) {
|
||||||
return n_unsent_bytes;
|
return (tx_head - tx_tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nonblocking byte receive.
|
/* Non-blocking byte receive.
|
||||||
*
|
*
|
||||||
* Copies up to len bytes from our private data buffer (*NOT* the PMA)
|
* Copies up to len bytes from our private data buffer (*NOT* the PMA)
|
||||||
* into buf and deq's the FIFO. */
|
* into buf and deq's the FIFO. */
|
||||||
uint32 usb_cdcacm_rx(uint8* buf, uint32 len) {
|
uint32 usb_cdcacm_rx(uint8* buf, uint32 len)
|
||||||
|
{
|
||||||
/* Copy bytes to buffer. */
|
/* Copy bytes to buffer. */
|
||||||
uint32 n_copied = usb_cdcacm_peek(buf, len);
|
uint32 n_copied = usb_cdcacm_peek(buf, len);
|
||||||
|
|
||||||
/* Mark bytes as read. */
|
/* Mark bytes as read. */
|
||||||
n_unread_bytes -= n_copied;
|
uint16 tail = rx_tail; // load volatile variable
|
||||||
rx_offset = (rx_offset + n_copied) % CDC_SERIAL_BUFFER_SIZE;
|
tail = (tail + n_copied) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
|
rx_tail = tail; // store volatile variable
|
||||||
|
|
||||||
/* If all bytes have been read, re-enable the RX endpoint, which
|
uint32 rx_unread = (rx_head - tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
* was set to NAK when the current batch of bytes was received. */
|
// If buffer was emptied to a pre-set value, re-enable the RX endpoint
|
||||||
if (n_unread_bytes == 0) {
|
if ( rx_unread <= 64 ) { // experimental value, gives the best performance
|
||||||
usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE);
|
|
||||||
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID);
|
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return n_copied;
|
return n_copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nonblocking byte lookahead.
|
/* Non-blocking byte lookahead.
|
||||||
*
|
*
|
||||||
* Looks at unread bytes without marking them as read. */
|
* Looks at unread bytes without marking them as read. */
|
||||||
uint32 usb_cdcacm_peek(uint8* buf, uint32 len) {
|
uint32 usb_cdcacm_peek(uint8* buf, uint32 len)
|
||||||
|
{
|
||||||
int i;
|
int i;
|
||||||
uint32 head = rx_offset;
|
uint32 tail = rx_tail;
|
||||||
|
uint32 rx_unread = (rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
|
|
||||||
if (len > n_unread_bytes) {
|
if (len > rx_unread) {
|
||||||
len = n_unread_bytes;
|
len = rx_unread;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
buf[i] = vcomBufferRx[head];
|
buf[i] = vcomBufferRx[tail];
|
||||||
head = (head + 1) % CDC_SERIAL_BUFFER_SIZE;
|
tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) {
|
uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len)
|
||||||
|
{
|
||||||
int i;
|
int i;
|
||||||
uint32 head = (rx_offset + offset) % CDC_SERIAL_BUFFER_SIZE;
|
uint32 tail = (rx_tail + offset) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
|
||||||
|
uint32 rx_unread = (rx_head-tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
|
|
||||||
if (len + offset > n_unread_bytes) {
|
if (len + offset > rx_unread) {
|
||||||
len = n_unread_bytes - offset;
|
len = rx_unread - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
buf[i] = vcomBufferRx[head];
|
buf[i] = vcomBufferRx[tail];
|
||||||
head = (head + 1) % CDC_SERIAL_BUFFER_SIZE;
|
tail = (tail + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
@ -495,12 +513,12 @@ uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) {
|
||||||
/* Roger Clark. Added. for Arduino 1.0 API support of Serial.peek() */
|
/* Roger Clark. Added. for Arduino 1.0 API support of Serial.peek() */
|
||||||
int usb_cdcacm_peek_char()
|
int usb_cdcacm_peek_char()
|
||||||
{
|
{
|
||||||
if (n_unread_bytes == 0)
|
if (usb_cdcacm_data_available() == 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vcomBufferRx[rx_offset];
|
return vcomBufferRx[rx_tail];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 usb_cdcacm_get_dtr() {
|
uint8 usb_cdcacm_get_dtr() {
|
||||||
|
@ -534,41 +552,75 @@ int usb_cdcacm_get_n_data_bits(void) {
|
||||||
return line_coding.bDataBits;
|
return line_coding.bDataBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callbacks
|
* Callbacks
|
||||||
*/
|
*/
|
||||||
|
static void vcomDataTxCb(void)
|
||||||
static void vcomDataTxCb(void) {
|
{
|
||||||
n_unsent_bytes = 0;
|
uint32 tail = tx_tail; // load volatile variable
|
||||||
transmitting = 0;
|
uint32 tx_unsent = (tx_head - tail) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||||
|
if (tx_unsent==0) {
|
||||||
|
if ( (--transmitting)==0) goto flush; // no more data to send
|
||||||
|
return; // it was already flushed, keep Tx endpoint disabled
|
||||||
|
}
|
||||||
|
transmitting = 1;
|
||||||
|
// We can only send up to USB_CDCACM_TX_EPSIZE bytes in the endpoint.
|
||||||
|
if (tx_unsent > USB_CDCACM_TX_EPSIZE) {
|
||||||
|
tx_unsent = USB_CDCACM_TX_EPSIZE;
|
||||||
|
}
|
||||||
|
// copy the bytes from USB Tx buffer to PMA buffer
|
||||||
|
uint32 *dst = usb_pma_ptr(USB_CDCACM_TX_ADDR);
|
||||||
|
uint16 tmp = 0;
|
||||||
|
uint16 val;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < tx_unsent; i++) {
|
||||||
|
val = vcomBufferTx[tail];
|
||||||
|
tail = (tail + 1) & CDC_SERIAL_TX_BUFFER_SIZE_MASK;
|
||||||
|
if (i&1) {
|
||||||
|
*dst++ = tmp | (val<<8);
|
||||||
|
} else {
|
||||||
|
tmp = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( tx_unsent&1 ) {
|
||||||
|
*dst = tmp;
|
||||||
|
}
|
||||||
|
tx_tail = tail; // store volatile variable
|
||||||
|
flush:
|
||||||
|
// enable Tx endpoint
|
||||||
|
usb_set_ep_tx_count(USB_CDCACM_TX_ENDP, tx_unsent);
|
||||||
|
usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_VALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vcomDataRxCb(void) {
|
|
||||||
uint32 ep_rx_size;
|
static void vcomDataRxCb(void)
|
||||||
uint32 tail = (rx_offset + n_unread_bytes) % CDC_SERIAL_BUFFER_SIZE;
|
{
|
||||||
uint8 ep_rx_data[USB_CDCACM_RX_EPSIZE];
|
uint32 head = rx_head; // load volatile variable
|
||||||
|
|
||||||
|
uint32 ep_rx_size = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP);
|
||||||
|
// This copy won't overwrite unread bytes as long as there is
|
||||||
|
// enough room in the USB Rx buffer for next packet
|
||||||
|
uint32 *src = usb_pma_ptr(USB_CDCACM_RX_ADDR);
|
||||||
|
uint16 tmp = 0;
|
||||||
|
uint8 val;
|
||||||
uint32 i;
|
uint32 i;
|
||||||
|
|
||||||
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_NAK);
|
|
||||||
ep_rx_size = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP);
|
|
||||||
/* This copy won't overwrite unread bytes, since we've set the RX
|
|
||||||
* endpoint to NAK, and will only set it to VALID when all bytes
|
|
||||||
* have been read. */
|
|
||||||
usb_copy_from_pma((uint8*)ep_rx_data, ep_rx_size,
|
|
||||||
USB_CDCACM_RX_ADDR);
|
|
||||||
|
|
||||||
for (i = 0; i < ep_rx_size; i++) {
|
for (i = 0; i < ep_rx_size; i++) {
|
||||||
vcomBufferRx[tail] = ep_rx_data[i];
|
if (i&1) {
|
||||||
tail = (tail + 1) % CDC_SERIAL_BUFFER_SIZE;
|
val = tmp>>8;
|
||||||
|
} else {
|
||||||
|
tmp = *src++;
|
||||||
|
val = tmp&0xFF;
|
||||||
|
}
|
||||||
|
vcomBufferRx[head] = val;
|
||||||
|
head = (head + 1) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
}
|
}
|
||||||
|
rx_head = head; // store volatile variable
|
||||||
|
|
||||||
n_unread_bytes += ep_rx_size;
|
uint32 rx_unread = (head - rx_tail) & CDC_SERIAL_RX_BUFFER_SIZE_MASK;
|
||||||
|
// only enable further Rx if there is enough room to receive one more packet
|
||||||
if ( n_unread_bytes == 0 ) {
|
if ( rx_unread < (CDC_SERIAL_RX_BUFFER_SIZE-USB_CDCACM_RX_EPSIZE) ) {
|
||||||
usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE);
|
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||||
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (rx_hook) {
|
if (rx_hook) {
|
||||||
rx_hook(USB_CDCACM_HOOK_RX, 0);
|
rx_hook(USB_CDCACM_HOOK_RX, 0);
|
||||||
|
@ -646,10 +698,11 @@ static void usbReset(void) {
|
||||||
SetDeviceAddress(0);
|
SetDeviceAddress(0);
|
||||||
|
|
||||||
/* Reset the RX/TX state */
|
/* Reset the RX/TX state */
|
||||||
n_unread_bytes = 0;
|
rx_head = 0;
|
||||||
n_unsent_bytes = 0;
|
rx_tail = 0;
|
||||||
rx_offset = 0;
|
tx_head = 0;
|
||||||
transmitting = 0;
|
tx_tail = 0;
|
||||||
|
transmitting = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static RESULT usbDataSetup(uint8 request) {
|
static RESULT usbDataSetup(uint8 request) {
|
||||||
|
|
|
@ -100,44 +100,23 @@ size_t n = 0;
|
||||||
|
|
||||||
size_t USBSerial::write(const char *str) {
|
size_t USBSerial::write(const char *str) {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
this->write(str, strlen(str));
|
this->write((const uint8*)str, strlen(str));
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t USBSerial::write(const void *buf, uint32 len) {
|
size_t USBSerial::write(const uint8 *buf, uint32 len)
|
||||||
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
if (!this->isConnected() || !buf) {
|
if (!this->isConnected() || !buf) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 txed = 0;
|
uint32 txed = 0;
|
||||||
uint32 old_txed = 0;
|
while (txed < len) {
|
||||||
uint32 start = millis();
|
txed += usb_cdcacm_tx((const uint8*)buf + txed, len - txed);
|
||||||
|
|
||||||
uint32 sent = 0;
|
|
||||||
|
|
||||||
while (txed < len && (millis() - start < USB_TIMEOUT)) {
|
|
||||||
sent = usb_cdcacm_tx((const uint8*)buf + txed, len - txed);
|
|
||||||
txed += sent;
|
|
||||||
if (old_txed != txed) {
|
|
||||||
start = millis();
|
|
||||||
}
|
|
||||||
old_txed = txed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
#if 0
|
|
||||||
// this code leads to a serious performance drop and appears to be
|
|
||||||
// unnecessary - everything seems to work fine without, -jcw, 2015-11-05
|
|
||||||
// see http://stm32duino.com/posting.php?mode=quote&f=3&p=7746
|
|
||||||
if (sent == USB_CDCACM_TX_EPSIZE) {
|
|
||||||
while (usb_cdcacm_is_transmitting() != 0) {
|
|
||||||
}
|
|
||||||
/* flush out to avoid having the pc wait for more data */
|
|
||||||
usb_cdcacm_tx(NULL, 0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int USBSerial::available(void) {
|
int USBSerial::available(void) {
|
||||||
|
@ -168,14 +147,10 @@ void USBSerial::flush(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 USBSerial::read(void *buf, uint32 len) {
|
uint32 USBSerial::read(uint8 * buf, uint32 len) {
|
||||||
if (!buf) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 rxed = 0;
|
uint32 rxed = 0;
|
||||||
while (rxed < len) {
|
while (rxed < len) {
|
||||||
rxed += usb_cdcacm_rx((uint8*)buf + rxed, len - rxed);
|
rxed += usb_cdcacm_rx(buf + rxed, len - rxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rxed;
|
return rxed;
|
||||||
|
@ -183,13 +158,13 @@ uint32 USBSerial::read(void *buf, uint32 len) {
|
||||||
|
|
||||||
/* Blocks forever until 1 byte is received */
|
/* Blocks forever until 1 byte is received */
|
||||||
int USBSerial::read(void) {
|
int USBSerial::read(void) {
|
||||||
uint8 b;
|
int8 b;
|
||||||
/*
|
/*
|
||||||
this->read(&b, 1);
|
this->read(&b, 1);
|
||||||
return b;
|
return b;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (usb_cdcacm_rx(&b, 1)==0)
|
if (usb_cdcacm_rx((uint8*)&b, 1)==0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ public:
|
||||||
|
|
||||||
virtual int available(void);// Changed to virtual
|
virtual int available(void);// Changed to virtual
|
||||||
|
|
||||||
uint32 read(void *buf, uint32 len);
|
uint32 read(uint8 * buf, uint32 len);
|
||||||
// uint8 read(void);
|
// uint8 read(void);
|
||||||
|
|
||||||
// Roger Clark. added functions to support Arduino 1.0 API
|
// Roger Clark. added functions to support Arduino 1.0 API
|
||||||
|
|
|
@ -349,8 +349,8 @@ static inline void usb_clear_status_out(uint8 ep) {
|
||||||
void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset);
|
void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset);
|
||||||
void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset);
|
void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset);
|
||||||
|
|
||||||
static inline void* usb_pma_ptr(uint32 offset) {
|
static inline uint32 * usb_pma_ptr(uint32 offset) {
|
||||||
return (void*)(USB_PMA_BASE + 2 * offset);
|
return (uint32*)(USB_PMA_BASE + 2 * offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue