* arch/arm/src/stm32/stm32_usbdev.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* - RM0008 Reference manual, STMicro document ID 13902
* - STM32F10xxx USB development kit, UM0424, STMicro
*/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/param.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <nuttx/irq.h>
#include "arm_internal.h"
#include "stm32.h"
#include "stm32_syscfg.h"
#include "stm32_gpio.h"
#include "stm32_usbdev.h"
#if defined(CONFIG_USBDEV) && defined(CONFIG_STM32_USB)
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_USBDEV_EP0_MAXSIZE
# define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif
#ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE
# define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE
#endif
#ifdef CONFIG_STM32_STM32F30XX
# ifdef CONFIG_STM32_USB_ITRMP
# define STM32_IRQ_USBHP STM32_IRQ_USBHP_2
# define STM32_IRQ_USBLP STM32_IRQ_USBLP_2
# define STM32_IRQ_USBWKUP STM32_IRQ_USBWKUP_2
# else
# define STM32_IRQ_USBHP STM32_IRQ_USBHP_1
# define STM32_IRQ_USBLP STM32_IRQ_USBLP_1
# define STM32_IRQ_USBWKUP STM32_IRQ_USBWKUP_1
# endif
#endif
* enabled.
*/
#ifndef CONFIG_DEBUG_USB_INFO
# undef CONFIG_STM32_USBDEV_REGDEBUG
#endif
#define STM32_CNTR_SETUP (USB_CNTR_RESETM|USB_CNTR_SUSPM|USB_CNTR_CTRM)
* bidirectional endpoints. However, when you take into account PMA buffer
* usage (see below) and the fact that EP0 is bidirectional, then there is
* a functional limitation of EP0 + 5 mono-directional endpoints = 6. We'll
* define STM32_NENDPOINTS to be 8, however, because that is how many
* endpoint register sets there are.
*/
#define EP0 (0)
#define EP1 (1)
#define EP2 (2)
#define EP3 (3)
#define EP4 (4)
#define EP5 (5)
#define EP6 (6)
#define EP7 (7)
#define STM32_ENDP_BIT(ep) (1 << (ep))
#define STM32_ENDP_ALLSET 0xff
#define STM32_MAXPACKET_SHIFT (6)
#define STM32_MAXPACKET_SIZE (1 << (STM32_MAXPACKET_SHIFT))
#define STM32_MAXPACKET_MASK (STM32_MAXPACKET_SIZE-1)
#define STM32_EP0MAXPACKET STM32_MAXPACKET_SIZE
* memory. The buffer table is positioned at the beginning of the 512-byte
* CAN/USB memory. We will use the first STM32_NENDPOINTS*4 words for the
* buffer table.
* That is exactly 64 bytes, leaving 7*64 bytes for endpoint buffers.
*/
#define STM32_BTABLE_ADDRESS (0x00)
#define STM32_DESC_SIZE (8)
#define STM32_BTABLE_SIZE (STM32_NENDPOINTS*STM32_DESC_SIZE)
* we have space for only 7 buffers; endpoint 0 will require two buffers,
* leaving 5 for other endpoints.
*/
#define STM32_BUFFER_START STM32_BTABLE_SIZE
#define STM32_EP0_RXADDR STM32_BUFFER_START
#define STM32_EP0_TXADDR (STM32_EP0_RXADDR+STM32_EP0MAXPACKET)
#define STM32_BUFFER_EP0 0x03
#define STM32_NBUFFERS 7
#define STM32_BUFFER_BIT(bn) (1 << (bn))
#define STM32_BUFFER_ALLSET 0x7f
#define STM32_BUFNO2BUF(bn) (STM32_BUFFER_START+((bn)<<STM32_MAXPACKET_SHIFT))
#define REQRECIPIENT_MASK (USB_REQ_TYPE_MASK | USB_REQ_RECIPIENT_MASK)
#define EPR_NOTOG_MASK (USB_EPR_CTR_RX | USB_EPR_SETUP | USB_EPR_EPTYPE_MASK |\
USB_EPR_EP_KIND | USB_EPR_CTR_TX | USB_EPR_EA_MASK)
#define EPR_TXDTOG_MASK (USB_EPR_STATTX_MASK | EPR_NOTOG_MASK)
#define EPR_RXDTOG_MASK (USB_EPR_STATRX_MASK | EPR_NOTOG_MASK)
#define stm32_rqempty(ep) ((ep)->head == NULL)
#define stm32_rqpeek(ep) ((ep)->head)
#define STM32_TRACEERR_ALLOCFAIL 0x0001
#define STM32_TRACEERR_BADCLEARFEATURE 0x0002
#define STM32_TRACEERR_BADDEVGETSTATUS 0x0003
#define STM32_TRACEERR_BADEPGETSTATUS 0x0004
#define STM32_TRACEERR_BADEPNO 0x0005
#define STM32_TRACEERR_BADEPTYPE 0x0006
#define STM32_TRACEERR_BADGETCONFIG 0x0007
#define STM32_TRACEERR_BADGETSETDESC 0x0008
#define STM32_TRACEERR_BADGETSTATUS 0x0009
#define STM32_TRACEERR_BADSETADDRESS 0x000a
#define STM32_TRACEERR_BADSETCONFIG 0x000b
#define STM32_TRACEERR_BADSETFEATURE 0x000c
#define STM32_TRACEERR_BINDFAILED 0x000d
#define STM32_TRACEERR_DISPATCHSTALL 0x000e
#define STM32_TRACEERR_DRIVER 0x000f
#define STM32_TRACEERR_DRIVERREGISTERED 0x0010
#define STM32_TRACEERR_EP0BADCTR 0x0011
#define STM32_TRACEERR_EP0SETUPSTALLED 0x0012
#define STM32_TRACEERR_EPBUFFER 0x0013
#define STM32_TRACEERR_EPDISABLED 0x0014
#define STM32_TRACEERR_EPOUTNULLPACKET 0x0015
#define STM32_TRACEERR_EPRESERVE 0x0016
#define STM32_TRACEERR_INVALIDCTRLREQ 0x0017
#define STM32_TRACEERR_INVALIDPARMS 0x0018
#define STM32_TRACEERR_IRQREGISTRATION 0x0019
#define STM32_TRACEERR_NOTCONFIGURED 0x001a
#define STM32_TRACEERR_REQABORTED 0x001b
#define STM32_TRACEINTID_CLEARFEATURE 0x0001
#define STM32_TRACEINTID_DEVGETSTATUS 0x0002
#define STM32_TRACEINTID_DISPATCH 0x0003
#define STM32_TRACEINTID_EP0IN 0x0004
#define STM32_TRACEINTID_EP0INDONE 0x0005
#define STM32_TRACEINTID_EP0OUTDONE 0x0006
#define STM32_TRACEINTID_EP0SETUPDONE 0x0007
#define STM32_TRACEINTID_EP0SETUPSETADDRESS 0x0008
#define STM32_TRACEINTID_EPGETSTATUS 0x0009
#define STM32_TRACEINTID_EPINDONE 0x000a
#define STM32_TRACEINTID_EPINQEMPTY 0x000b
#define STM32_TRACEINTID_EPOUTDONE 0x000c
#define STM32_TRACEINTID_EPOUTPENDING 0x000d
#define STM32_TRACEINTID_EPOUTQEMPTY 0x000e
#define STM32_TRACEINTID_ESOF 0x000f
#define STM32_TRACEINTID_GETCONFIG 0x0010
#define STM32_TRACEINTID_GETSETDESC 0x0011
#define STM32_TRACEINTID_GETSETIF 0x0012
#define STM32_TRACEINTID_GETSTATUS 0x0013
#define STM32_TRACEINTID_HPINTERRUPT 0x0014
#define STM32_TRACEINTID_IFGETSTATUS 0x0015
#define STM32_TRACEINTID_LPCTR 0x0016
#define STM32_TRACEINTID_LPINTERRUPT 0x0017
#define STM32_TRACEINTID_NOSTDREQ 0x0018
#define STM32_TRACEINTID_RESET 0x0019
#define STM32_TRACEINTID_SETCONFIG 0x001a
#define STM32_TRACEINTID_SETFEATURE 0x001b
#define STM32_TRACEINTID_SUSP 0x001c
#define STM32_TRACEINTID_SYNCHFRAME 0x001d
#define STM32_TRACEINTID_WKUP 0x001e
#define STM32_TRACEINTID_EP0SETUPOUT 0x001f
#define STM32_TRACEINTID_EP0SETUPOUTDATA 0x0020
#ifdef CONFIG_ENDIAN_BIG
# define LSB 1
# define MSB 0
#else
# define LSB 0
# define MSB 1
#endif
* Private Type Definitions
****************************************************************************/
enum stm32_ep0state_e
{
EP0STATE_IDLE = 0,
EP0STATE_SETUP_OUT,
EP0STATE_SETUP_READY,
* now the data has arrived */
EP0STATE_WRREQUEST,
EP0STATE_RDREQUEST,
EP0STATE_STALLED
};
enum stm32_rsmstate_e
{
RSMSTATE_IDLE = 0,
RSMSTATE_STARTED,
RSMSTATE_WAITING
};
union wb_u
{
uint16_t w;
uint8_t b[2];
};
struct stm32_req_s
{
struct usbdev_req_s req;
struct stm32_req_s *flink;
};
struct stm32_ep_s
{
* structure so that it is possible to simply cast from struct usbdev_ep_s
* to struct stm32_ep_s.
*/
struct usbdev_ep_s ep;
struct stm32_usbdev_s *dev;
struct stm32_req_s *head;
struct stm32_req_s *tail;
uint8_t bufno;
uint8_t stalled:1;
uint8_t halted:1;
uint8_t txbusy:1;
uint8_t txnullpkt:1;
};
struct stm32_usbdev_s
{
* structure so that it is possible to simply cast from struct usbdev_s
* to structstm32_usbdev_s.
*/
struct usbdev_s usbdev;
struct usbdevclass_driver_s *driver;
uint8_t ep0state;
uint8_t rsmstate;
uint8_t nesofs;
uint8_t rxpending:1;
uint8_t selfpowered:1;
uint8_t epavail;
uint8_t bufavail;
uint16_t rxstatus;
uint16_t txstatus;
uint16_t imask;
*
* ctrl
* The 8-byte SETUP request is received on the EP0 OUT endpoint and is
* saved.
*
* ep0data
* For OUT SETUP requests, the SETUP data phase must also complete
* before the SETUP command can be processed. The ep0 packet receipt
* logic stm32_ep0_rdrequest will save the accompanying EP0 OUT data
* in ep0data[] before the SETUP command is re-processed.
*
* ep0datlen
* Length of OUT DATA received in ep0data[]
*/
struct usb_ctrlreq_s ctrl;
uint8_t ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE];
uint16_t ep0datlen;
struct stm32_ep_s eplist[STM32_NENDPOINTS];
};
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_STM32_USBDEV_REGDEBUG
static uint16_t stm32_getreg(uint32_t addr);
static void stm32_putreg(uint16_t val, uint32_t addr);
static void stm32_checksetup(void);
static void stm32_dumpep(int epno);
#else
# define stm32_getreg(addr) getreg16(addr)
# define stm32_putreg(val,addr) putreg16(val,addr)
# define stm32_checksetup()
# define stm32_dumpep(epno)
#endif
static inline void
stm32_seteptxcount(uint8_t epno, uint16_t count);
static inline void
stm32_seteptxaddr(uint8_t epno, uint16_t addr);
static inline uint16_t
stm32_geteptxaddr(uint8_t epno);
static void stm32_seteprxcount(uint8_t epno, uint16_t count);
static inline uint16_t
stm32_geteprxcount(uint8_t epno);
static inline void
stm32_seteprxaddr(uint8_t epno, uint16_t addr);
static inline uint16_t
stm32_geteprxaddr(uint8_t epno);
static inline void
stm32_setepaddress(uint8_t epno, uint16_t addr);
static inline void
stm32_seteptype(uint8_t epno, uint16_t type);
static inline void
stm32_seteptxaddr(uint8_t epno, uint16_t addr);
static inline void
stm32_clrstatusout(uint8_t epno);
static void stm32_clrrxdtog(uint8_t epno);
static void stm32_clrtxdtog(uint8_t epno);
static void stm32_clrepctrrx(uint8_t epno);
static void stm32_clrepctrtx(uint8_t epno);
static void stm32_seteptxstatus(uint8_t epno, uint16_t state);
static void stm32_seteprxstatus(uint8_t epno, uint16_t state);
static inline uint16_t
stm32_geteptxstatus(uint8_t epno);
static inline uint16_t
stm32_geteprxstatus(uint8_t epno);
static bool stm32_eptxstalled(uint8_t epno);
static bool stm32_eprxstalled(uint8_t epno);
static void stm32_setimask(struct stm32_usbdev_s *priv,
uint16_t setbits,
uint16_t clrbits);
static void stm32_suspend(struct stm32_usbdev_s *priv);
static void stm32_initresume(struct stm32_usbdev_s *priv);
static void stm32_esofpoll(struct stm32_usbdev_s *priv) ;
static void stm32_copytopma(const uint8_t *buffer, uint16_t pma,
uint16_t nbytes);
static inline void stm32_copyfrompma(uint8_t *buffer,
uint16_t pma, uint16_t nbytes);
static struct stm32_req_s *
stm32_rqdequeue(struct stm32_ep_s *privep);
static void stm32_rqenqueue(struct stm32_ep_s *privep,
struct stm32_req_s *req);
static inline void
stm32_abortrequest(struct stm32_ep_s *privep,
struct stm32_req_s *privreq, int16_t result);
static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result);
static void stm32_epwrite(struct stm32_usbdev_s *buf,
struct stm32_ep_s *privep,
const uint8_t *data, uint32_t nbytes);
static int stm32_wrrequest(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep);
inline static int
stm32_wrrequest_ep0(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep);
static inline int
stm32_ep0_rdrequest(struct stm32_usbdev_s *priv);
static int stm32_rdrequest(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep);
static void stm32_cancelrequests(struct stm32_ep_s *privep);
static void stm32_dispatchrequest(struct stm32_usbdev_s *priv);
static void stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno);
static void stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value);
static void stm32_ep0setup(struct stm32_usbdev_s *priv);
static void stm32_ep0out(struct stm32_usbdev_s *priv);
static void stm32_ep0in(struct stm32_usbdev_s *priv);
static inline void
stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr);
static void stm32_lptransfer(struct stm32_usbdev_s *priv);
static int stm32_hpinterrupt(int irq, void *context, void *arg);
static int stm32_lpinterrupt(int irq, void *context, void *arg);
static inline struct stm32_ep_s *
stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset);
static inline void
stm32_epunreserve(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep);
static inline bool
stm32_epreserved(struct stm32_usbdev_s *priv, int epno);
static int stm32_epallocpma(struct stm32_usbdev_s *priv);
static inline void
stm32_epfreepma(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep);
static int stm32_epconfigure(struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc, bool last);
static int stm32_epdisable(struct usbdev_ep_s *ep);
static struct usbdev_req_s *
stm32_epallocreq(struct usbdev_ep_s *ep);
static void stm32_epfreereq(struct usbdev_ep_s *ep,
struct usbdev_req_s *);
static int stm32_epsubmit(struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int stm32_epcancel(struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int stm32_epstall(struct usbdev_ep_s *ep, bool resume);
static struct usbdev_ep_s *
stm32_allocep(struct usbdev_s *dev, uint8_t epno, bool in,
uint8_t eptype);
static void stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep);
static int stm32_getframe(struct usbdev_s *dev);
static int stm32_wakeup(struct usbdev_s *dev);
static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered);
static void stm32_reset(struct stm32_usbdev_s *priv);
static void stm32_hwreset(struct stm32_usbdev_s *priv);
static void stm32_hwsetup(struct stm32_usbdev_s *priv);
static void stm32_hwshutdown(struct stm32_usbdev_s *priv);
* Private Data
****************************************************************************/
* be simply retained in a single global instance.
*/
static struct stm32_usbdev_s g_usbdev;
static const struct usbdev_epops_s g_epops =
{
.configure = stm32_epconfigure,
.disable = stm32_epdisable,
.allocreq = stm32_epallocreq,
.freereq = stm32_epfreereq,
.submit = stm32_epsubmit,
.cancel = stm32_epcancel,
.stall = stm32_epstall,
};
static const struct usbdev_ops_s g_devops =
{
.allocep = stm32_allocep,
.freeep = stm32_freeep,
.getframe = stm32_getframe,
.wakeup = stm32_wakeup,
.selfpowered = stm32_selfpowered,
.pullup = stm32_usbpullup,
};
* Public Data
****************************************************************************/
#ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_intdecode[] =
{
TRACE_STR(STM32_TRACEINTID_CLEARFEATURE),
TRACE_STR(STM32_TRACEINTID_DEVGETSTATUS),
TRACE_STR(STM32_TRACEINTID_DISPATCH),
TRACE_STR(STM32_TRACEINTID_EP0IN),
TRACE_STR(STM32_TRACEINTID_EP0INDONE),
TRACE_STR(STM32_TRACEINTID_EP0OUTDONE),
TRACE_STR(STM32_TRACEINTID_EP0SETUPDONE),
TRACE_STR(STM32_TRACEINTID_EP0SETUPSETADDRESS),
TRACE_STR(STM32_TRACEINTID_EPGETSTATUS),
TRACE_STR(STM32_TRACEINTID_EPINDONE),
TRACE_STR(STM32_TRACEINTID_EPINQEMPTY),
TRACE_STR(STM32_TRACEINTID_EPOUTDONE),
TRACE_STR(STM32_TRACEINTID_EPOUTPENDING),
TRACE_STR(STM32_TRACEINTID_EPOUTQEMPTY),
TRACE_STR(STM32_TRACEINTID_ESOF),
TRACE_STR(STM32_TRACEINTID_GETCONFIG),
TRACE_STR(STM32_TRACEINTID_GETSETDESC),
TRACE_STR(STM32_TRACEINTID_GETSETIF),
TRACE_STR(STM32_TRACEINTID_GETSTATUS),
TRACE_STR(STM32_TRACEINTID_HPINTERRUPT),
TRACE_STR(STM32_TRACEINTID_IFGETSTATUS),
TRACE_STR(STM32_TRACEINTID_LPCTR),
TRACE_STR(STM32_TRACEINTID_LPINTERRUPT),
TRACE_STR(STM32_TRACEINTID_NOSTDREQ),
TRACE_STR(STM32_TRACEINTID_RESET),
TRACE_STR(STM32_TRACEINTID_SETCONFIG),
TRACE_STR(STM32_TRACEINTID_SETFEATURE),
TRACE_STR(STM32_TRACEINTID_SUSP),
TRACE_STR(STM32_TRACEINTID_SYNCHFRAME),
TRACE_STR(STM32_TRACEINTID_WKUP),
TRACE_STR(STM32_TRACEINTID_EP0SETUPOUT),
TRACE_STR(STM32_TRACEINTID_EP0SETUPOUTDATA),
TRACE_STR_END
};
#endif
#ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_deverror[] =
{
TRACE_STR(STM32_TRACEERR_ALLOCFAIL),
TRACE_STR(STM32_TRACEERR_BADCLEARFEATURE),
TRACE_STR(STM32_TRACEERR_BADDEVGETSTATUS),
TRACE_STR(STM32_TRACEERR_BADEPGETSTATUS),
TRACE_STR(STM32_TRACEERR_BADEPNO),
TRACE_STR(STM32_TRACEERR_BADEPTYPE),
TRACE_STR(STM32_TRACEERR_BADGETCONFIG),
TRACE_STR(STM32_TRACEERR_BADGETSETDESC),
TRACE_STR(STM32_TRACEERR_BADGETSTATUS),
TRACE_STR(STM32_TRACEERR_BADSETADDRESS),
TRACE_STR(STM32_TRACEERR_BADSETCONFIG),
TRACE_STR(STM32_TRACEERR_BADSETFEATURE),
TRACE_STR(STM32_TRACEERR_BINDFAILED),
TRACE_STR(STM32_TRACEERR_DISPATCHSTALL),
TRACE_STR(STM32_TRACEERR_DRIVER),
TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED),
TRACE_STR(STM32_TRACEERR_EP0BADCTR),
TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED),
TRACE_STR(STM32_TRACEERR_EPBUFFER),
TRACE_STR(STM32_TRACEERR_EPDISABLED),
TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET),
TRACE_STR(STM32_TRACEERR_EPRESERVE),
TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ),
TRACE_STR(STM32_TRACEERR_INVALIDPARMS),
TRACE_STR(STM32_TRACEERR_IRQREGISTRATION),
TRACE_STR(STM32_TRACEERR_NOTCONFIGURED),
TRACE_STR(STM32_TRACEERR_REQABORTED),
TRACE_STR_END
};
#endif
* Private Private Functions
****************************************************************************/
* Register Operations
****************************************************************************/
* Name: stm32_getreg
****************************************************************************/
#ifdef CONFIG_STM32_USBDEV_REGDEBUG
static uint16_t stm32_getreg(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint16_t preval = 0;
static uint32_t count = 0;
uint16_t val = getreg16(addr);
* Are we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr && val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
uinfo("...\n");
}
return val;
}
}
else
{
if (count > 3)
{
uinfo("[repeats %d more times]\n", count - 3);
}
prevaddr = addr;
preval = val;
count = 1;
}
uinfo("%08x->%04x\n", addr, val);
return val;
}
#endif
* Name: stm32_putreg
****************************************************************************/
#ifdef CONFIG_STM32_USBDEV_REGDEBUG
static void stm32_putreg(uint16_t val, uint32_t addr)
{
uinfo("%08x<-%04x\n", addr, val);
putreg16(val, addr);
}
#endif
* Name: stm32_dumpep
****************************************************************************/
#ifdef CONFIG_STM32_USBDEV_REGDEBUG
static void stm32_dumpep(int epno)
{
uint32_t addr;
uinfo("CNTR: %04x\n", getreg16(STM32_USB_CNTR));
uinfo("ISTR: %04x\n", getreg16(STM32_USB_ISTR));
uinfo("FNR: %04x\n", getreg16(STM32_USB_FNR));
uinfo("DADDR: %04x\n", getreg16(STM32_USB_DADDR));
uinfo("BTABLE: %04x\n", getreg16(STM32_USB_BTABLE));
addr = STM32_USB_EPR(epno);
uinfo("EPR%d: [%08x] %04x\n", epno, addr, getreg16(addr));
addr = STM32_USB_BTABLE_ADDR(epno, 0);
uinfo("DESC: %08x\n", addr);
addr = STM32_USB_ADDR_TX(epno);
uinfo(" TX ADDR: [%08x] %04x\n", addr, getreg16(addr));
addr = STM32_USB_COUNT_TX(epno);
uinfo(" COUNT: [%08x] %04x\n", addr, getreg16(addr));
addr = STM32_USB_ADDR_RX(epno);
uinfo(" RX ADDR: [%08x] %04x\n", addr, getreg16(addr));
addr = STM32_USB_COUNT_RX(epno);
uinfo(" COUNT: [%08x] %04x\n", addr, getreg16(addr));
}
#endif
* Name: stm32_checksetup
****************************************************************************/
#ifdef CONFIG_STM32_USBDEV_REGDEBUG
static void stm32_checksetup(void)
{
uint32_t cfgr = getreg32(STM32_RCC_CFGR);
uint32_t apb1rstr = getreg32(STM32_RCC_APB1RSTR);
uint32_t apb1enr = getreg32(STM32_RCC_APB1ENR);
uinfo("CFGR: %08x APB1RSTR: %08x APB1ENR: %08x\n",
cfgr, apb1rstr, apb1enr);
if ((apb1rstr & RCC_APB1RSTR_USBRST) != 0 ||
(apb1enr & RCC_APB1ENR_USBEN) == 0)
{
uerr("ERROR: USB is NOT setup correctly\n");
}
}
#endif
* Low-Level Helpers
****************************************************************************/
* Name: stm32_seteptxcount
****************************************************************************/
static inline void stm32_seteptxcount(uint8_t epno, uint16_t count)
{
volatile uint32_t *epaddr = (uint32_t *)STM32_USB_COUNT_TX(epno);
*epaddr = count;
}
* Name: stm32_seteptxaddr
****************************************************************************/
static inline void stm32_seteptxaddr(uint8_t epno, uint16_t addr)
{
volatile uint32_t *txaddr = (uint32_t *)STM32_USB_ADDR_TX(epno);
*txaddr = addr;
}
* Name: stm32_geteptxaddr
****************************************************************************/
static inline uint16_t stm32_geteptxaddr(uint8_t epno)
{
volatile uint32_t *txaddr = (uint32_t *)STM32_USB_ADDR_TX(epno);
return (uint16_t)*txaddr;
}
* Name: stm32_seteprxcount
****************************************************************************/
static void stm32_seteprxcount(uint8_t epno, uint16_t count)
{
volatile uint32_t *epaddr = (uint32_t *)STM32_USB_COUNT_RX(epno);
uint32_t rxcount = 0;
uint16_t nblocks;
* RX buffer. This is based on a block size of 2 or 32:
*
* USB_COUNT_RX_BL_SIZE not set:
* nblocks is in units of 2 bytes.
* 00000 - not allowed
* 00001 - 2 bytes
* ....
* 11111 - 62 bytes
*
* USB_COUNT_RX_BL_SIZE set:
* 00000 - 32 bytes
* 00001 - 64 bytes
* ...
* 01111 - 512 bytes
* 1xxxx - Not allowed
*/
if (count > 62)
{
nblocks = (count >> 5) - 1 ;
DEBUGASSERT(nblocks <= 0x0f);
rxcount = (uint32_t)((nblocks <<
USB_COUNT_RX_NUM_BLOCK_SHIFT) | USB_COUNT_RX_BL_SIZE);
}
else if (count > 0)
{
nblocks = (count + 1) >> 1;
DEBUGASSERT(nblocks > 0 && nblocks < 0x1f);
rxcount = (uint32_t)(nblocks << USB_COUNT_RX_NUM_BLOCK_SHIFT);
}
*epaddr = rxcount;
}
* Name: stm32_geteprxcount
****************************************************************************/
static inline uint16_t stm32_geteprxcount(uint8_t epno)
{
volatile uint32_t *epaddr = (uint32_t *)STM32_USB_COUNT_RX(epno);
return (*epaddr) & USB_COUNT_RX_MASK;
}
* Name: stm32_seteprxaddr
****************************************************************************/
static inline void stm32_seteprxaddr(uint8_t epno, uint16_t addr)
{
volatile uint32_t *rxaddr = (uint32_t *)STM32_USB_ADDR_RX(epno);
*rxaddr = addr;
}
* Name: stm32_seteprxaddr
****************************************************************************/
static inline uint16_t stm32_geteprxaddr(uint8_t epno)
{
volatile uint32_t *rxaddr = (uint32_t *)STM32_USB_ADDR_RX(epno);
return (uint16_t)*rxaddr;
}
* Name: stm32_setepaddress
****************************************************************************/
static inline void stm32_setepaddress(uint8_t epno, uint16_t addr)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
regval = stm32_getreg(epaddr);
regval &= EPR_NOTOG_MASK;
regval &= ~USB_EPR_EA_MASK;
regval |= (addr << USB_EPR_EA_SHIFT);
stm32_putreg(regval, epaddr);
}
* Name: stm32_seteptype
****************************************************************************/
static inline void stm32_seteptype(uint8_t epno, uint16_t type)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
regval = stm32_getreg(epaddr);
regval &= EPR_NOTOG_MASK;
regval &= ~USB_EPR_EPTYPE_MASK;
regval |= type;
stm32_putreg(regval, epaddr);
}
* Name: stm32_clrstatusout
****************************************************************************/
static inline void stm32_clrstatusout(uint8_t epno)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
* for a CONTROL endpoint, it is set to indicate that a status OUT
* transaction is expected. The bit is not used with out endpoint types.
*/
regval = stm32_getreg(epaddr);
regval &= EPR_NOTOG_MASK;
regval &= ~USB_EPR_EP_KIND;
stm32_putreg(regval, epaddr);
}
* Name: stm32_clrrxdtog
****************************************************************************/
static void stm32_clrrxdtog(uint8_t epno)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
regval = stm32_getreg(epaddr);
if ((regval & USB_EPR_DTOG_RX) != 0)
{
regval &= EPR_NOTOG_MASK;
regval |= USB_EPR_DTOG_RX;
stm32_putreg(regval, epaddr);
}
}
* Name: stm32_clrtxdtog
****************************************************************************/
static void stm32_clrtxdtog(uint8_t epno)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
regval = stm32_getreg(epaddr);
if ((regval & USB_EPR_DTOG_TX) != 0)
{
regval &= EPR_NOTOG_MASK;
regval |= USB_EPR_DTOG_TX;
stm32_putreg(regval, epaddr);
}
}
* Name: stm32_clrepctrrx
****************************************************************************/
static void stm32_clrepctrrx(uint8_t epno)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
regval = stm32_getreg(epaddr);
regval &= EPR_NOTOG_MASK;
regval &= ~USB_EPR_CTR_RX;
stm32_putreg(regval, epaddr);
}
* Name: stm32_clrepctrtx
****************************************************************************/
static void stm32_clrepctrtx(uint8_t epno)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
regval = stm32_getreg(epaddr);
regval &= EPR_NOTOG_MASK;
regval &= ~USB_EPR_CTR_TX;
stm32_putreg(regval, epaddr);
}
* Name: stm32_geteptxstatus
****************************************************************************/
static inline uint16_t stm32_geteptxstatus(uint8_t epno)
{
return (uint16_t)(stm32_getreg(STM32_USB_EPR(epno)) &
USB_EPR_STATTX_MASK);
}
* Name: stm32_geteprxstatus
****************************************************************************/
static inline uint16_t stm32_geteprxstatus(uint8_t epno)
{
return (stm32_getreg(STM32_USB_EPR(epno)) & USB_EPR_STATRX_MASK);
}
* Name: stm32_seteptxstatus
****************************************************************************/
static void stm32_seteptxstatus(uint8_t epno, uint16_t state)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
* value. When set to 0, the value remains unchanged; when set to one,
* value toggles.
*/
regval = stm32_getreg(epaddr);
* from the bits requested in 'state'
*/
regval ^= state;
regval &= EPR_TXDTOG_MASK;
stm32_putreg(regval, epaddr);
}
* Name: stm32_seteprxstatus
****************************************************************************/
static void stm32_seteprxstatus(uint8_t epno, uint16_t state)
{
uint32_t epaddr = STM32_USB_EPR(epno);
uint16_t regval;
* value. When set to 0, the value remains unchanged; when set to one,
* value toggles.
*/
regval = stm32_getreg(epaddr);
* from the bits requested in 'state'
*/
regval ^= state;
regval &= EPR_RXDTOG_MASK;
stm32_putreg(regval, epaddr);
}
* Name: stm32_eptxstalled
****************************************************************************/
static inline bool stm32_eptxstalled(uint8_t epno)
{
return (stm32_geteptxstatus(epno) == USB_EPR_STATTX_STALL);
}
* Name: stm32_eprxstalled
****************************************************************************/
static inline bool stm32_eprxstalled(uint8_t epno)
{
return (stm32_geteprxstatus(epno) == USB_EPR_STATRX_STALL);
}
* Request Helpers
****************************************************************************/
* Name: stm32_copytopma
****************************************************************************/
static void stm32_copytopma(const uint8_t *buffer,
uint16_t pma, uint16_t nbytes)
{
uint16_t *dest;
uint16_t ms;
uint16_t ls;
int nwords = (nbytes + 1) >> 1;
int i;
dest = (uint16_t *)(STM32_USBRAM_BASE + ((uint32_t)pma << 1));
for (i = nwords; i != 0; i--)
{
ls = (uint16_t)(*buffer++);
ms = (uint16_t)(*buffer++);
*dest = ms << 8 | ls;
* increments by 2*sizeof(uint16_t) = 4.
*/
dest += 2;
}
}
* Name: stm32_copyfrompma
****************************************************************************/
static inline void
stm32_copyfrompma(uint8_t *buffer, uint16_t pma, uint16_t nbytes)
{
uint32_t *src;
int nwords = (nbytes + 1) >> 1;
int i;
src = (uint32_t *)(STM32_USBRAM_BASE + ((uint32_t)pma << 1));
for (i = nwords; i != 0; i--)
{
*(uint16_t *)buffer = *src++;
* increments by 2*sizeof(uint8_t) = 2.
*/
buffer += 2;
}
}
* Name: stm32_rqdequeue
****************************************************************************/
static struct stm32_req_s *stm32_rqdequeue(struct stm32_ep_s *privep)
{
struct stm32_req_s *ret = privep->head;
if (ret)
{
privep->head = ret->flink;
if (!privep->head)
{
privep->tail = NULL;
}
ret->flink = NULL;
}
return ret;
}
* Name: stm32_rqenqueue
****************************************************************************/
static void stm32_rqenqueue(struct stm32_ep_s *privep,
struct stm32_req_s *req)
{
req->flink = NULL;
if (!privep->head)
{
privep->head = req;
privep->tail = req;
}
else
{
privep->tail->flink = req;
privep->tail = req;
}
}
* Name: stm32_abortrequest
****************************************************************************/
static inline void
stm32_abortrequest(struct stm32_ep_s *privep,
struct stm32_req_s *privreq, int16_t result)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_REQABORTED),
(uint16_t)USB_EPNO(privep->ep.eplog));
privreq->req.result = result;
privreq->req.callback(&privep->ep, &privreq->req);
}
* Name: stm32_reqcomplete
****************************************************************************/
static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result)
{
struct stm32_req_s *privreq;
irqstate_t flags;
flags = enter_critical_section();
privreq = stm32_rqdequeue(privep);
leave_critical_section(flags);
if (privreq)
{
* in the callback.
*/
bool stalled = privep->stalled;
if (USB_EPNO(privep->ep.eplog) == EP0)
{
privep->stalled = (privep->dev->ep0state == EP0STATE_STALLED);
}
privreq->req.result = result;
privreq->flink = NULL;
privreq->req.callback(&privep->ep, &privreq->req);
privep->stalled = stalled;
}
}
* Name: tm32_epwrite
****************************************************************************/
static void stm32_epwrite(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep,
const uint8_t *buf, uint32_t nbytes)
{
uint8_t epno = USB_EPNO(privep->ep.eplog);
usbtrace(TRACE_WRITE(epno), nbytes);
if (nbytes > 0)
{
* endpoint
*/
stm32_copytopma(buf, stm32_geteptxaddr(epno), nbytes);
}
stm32_seteptxcount(epno, nbytes);
priv->txstatus = USB_EPR_STATTX_VALID;
* cleared when the next data out interrupt is received.
*/
privep->txbusy = true;
}
* Name: stm32_wrrequest_ep0
*
* Description:
* Handle the ep0 state on writes.
*
****************************************************************************/
inline static int stm32_wrrequest_ep0(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep)
{
int ret;
ret = stm32_wrrequest(priv, privep);
priv->ep0state = ((ret == OK) ? EP0STATE_WRREQUEST : EP0STATE_IDLE);
return ret;
}
* Name: stm32_wrrequest
****************************************************************************/
static int stm32_wrrequest(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep)
{
struct stm32_req_s *privreq;
uint8_t *buf;
uint8_t epno;
int nbytes;
int bytesleft;
* there is no TX transfer in progress.
*/
privep->txbusy = false;
privreq = stm32_rqpeek(privep);
if (!privreq)
{
* requests to send.
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINQEMPTY), 0);
return -ENOENT;
}
epno = USB_EPNO(privep->ep.eplog);
uinfo("epno=%d req=%p: len=%zu xfrd=%zu nullpkt=%d\n",
epno, privreq, privreq->req.len,
privreq->req.xfrd, privep->txnullpkt);
UNUSED(epno);
bytesleft = privreq->req.len - privreq->req.xfrd;
nbytes = bytesleft;
#warning "REVISIT: If the EP supports double buffering, then we can do better"
* (because txnullpkt == 1 && nbytes == 0), or (2) we have not yet send
* the last packet (nbytes > 0).
* In either case, it is appropriate to clearn txnullpkt now.
*/
privep->txnullpkt = 0;
* and check if we need to send a following NULL packet.
*/
if (nbytes > 0)
{
* the request.
*/
if (nbytes >= privep->ep.maxpacket)
{
nbytes = privep->ep.maxpacket;
* maxpacketsize. Do we need to send a zero-length packet
* in this case?
*/
if (bytesleft == privep->ep.maxpacket &&
(privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
{
privep->txnullpkt = 1;
}
}
}
buf = privreq->req.buf + privreq->req.xfrd;
stm32_epwrite(priv, privep, buf, nbytes);
privreq->req.xfrd += nbytes;
bytesleft = privreq->req.len - privreq->req.xfrd;
* then we are finished with the request buffer).
*/
if (bytesleft == 0 && !privep->txnullpkt)
{
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
privreq->req.xfrd);
privep->txnullpkt = 0;
stm32_reqcomplete(privep, OK);
}
return OK;
}
* Name: stm32_ep0_rdrequest
*
* Description:
* This function is called from the stm32_ep0out handler when the ep0state
* is EP0STATE_SETUP_OUT and upon new incoming data is available in the
* endpoint 0's buffer.
* This function will simply copy the OUT data into ep0data.
*
****************************************************************************/
static inline int stm32_ep0_rdrequest(struct stm32_usbdev_s *priv)
{
uint32_t src;
int pmalen;
int readlen;
pmalen = stm32_geteprxcount(EP0);
uinfo("EP0: pmalen=%d\n", pmalen);
usbtrace(TRACE_READ(EP0), pmalen);
readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, pmalen);
src = stm32_geteprxaddr(EP0);
stm32_copyfrompma(&priv->ep0data[0], src, readlen);
priv->ep0state = EP0STATE_SETUP_READY;
priv->ep0datlen = readlen;
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUTDATA),
readlen);
stm32_ep0setup(priv);
priv->ep0datlen = 0;
return OK;
}
* Name: stm32_rdrequest
****************************************************************************/
static int stm32_rdrequest(struct stm32_usbdev_s *priv,
struct stm32_ep_s *privep)
{
struct stm32_req_s *privreq;
uint32_t src;
uint8_t *dest;
uint8_t epno;
int pmalen;
int readlen;
epno = USB_EPNO(privep->ep.eplog);
privreq = stm32_rqpeek(privep);
if (!privreq)
{
* Mark that the RX data is pending and hope that a packet is returned
* soon.
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTQEMPTY), epno);
return -ENOENT;
}
uinfo("EP%d: len=%zu xfrd=%zu\n",
epno, privreq->req.len, privreq->req.xfrd);
if (privreq->req.len == 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0);
stm32_reqcomplete(privep, OK);
return OK;
}
usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
dest = privreq->req.buf + privreq->req.xfrd;
src = stm32_geteprxaddr(epno);
pmalen = stm32_geteprxcount(epno);
readlen = MIN(privreq->req.len, pmalen);
stm32_copyfrompma(dest, src, readlen);
* then we are finished with the request buffer).
*/
privreq->req.xfrd += readlen;
if (pmalen < privep->ep.maxpacket || privreq->req.xfrd >= privreq->req.len)
{
usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd);
stm32_reqcomplete(privep, OK);
}
return OK;
}
* Name: stm32_cancelrequests
****************************************************************************/
static void stm32_cancelrequests(struct stm32_ep_s *privep)
{
while (!stm32_rqempty(privep))
{
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
(stm32_rqpeek(privep))->req.xfrd);
stm32_reqcomplete(privep, -ESHUTDOWN);
}
}
* Interrupt Level Processing
****************************************************************************/
* Name: stm32_dispatchrequest
****************************************************************************/
static void stm32_dispatchrequest(struct stm32_usbdev_s *priv)
{
int ret;
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0);
if (priv && priv->driver)
{
ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl,
priv->ep0data, priv->ep0datlen);
if (ret < 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0);
priv->ep0state = EP0STATE_STALLED;
}
}
}
* Name: stm32_epdone
****************************************************************************/
static void stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno)
{
struct stm32_ep_s *privep;
uint16_t epr;
epr = stm32_getreg(STM32_USB_EPR(epno));
privep = &priv->eplist[epno];
* CTR_RX is set by the hardware when an OUT/SETUP transaction
* successfully completed on this endpoint.
*/
if ((epr & USB_EPR_CTR_RX) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTDONE), epr);
* accept the host data.
*/
if (!stm32_rqempty(privep))
{
stm32_rdrequest(priv, privep);
* should set the STAT_RX bits to '11' (Valid) in the USB_EPnR,
* enabling further transactions. "
*/
priv->rxstatus = USB_EPR_STATRX_VALID;
}
if (stm32_rqempty(privep))
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTPENDING),
(uint16_t)epno);
* on this endpoint. "While the STAT_RX bits are equal to '10'
* (NAK), any OUT request addressed to that endpoint is NAKed,
* indicating a flow control condition: the USB host will retry
* the transaction until it succeeds."
*/
priv->rxstatus = USB_EPR_STATRX_NAK;
priv->rxpending = true;
}
stm32_clrepctrrx(epno);
stm32_seteprxstatus(epno, priv->rxstatus);
}
* CTR_TX is set when an IN transaction successfully completes on
* an endpoint
*/
else if ((epr & USB_EPR_CTR_TX) != 0)
{
stm32_clrepctrtx(epno);
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINDONE), epr);
priv->txstatus = USB_EPR_STATTX_NAK;
if (epno == EP0)
{
stm32_wrrequest_ep0(priv, privep);
}
else
{
stm32_wrrequest(priv, privep);
}
stm32_seteptxstatus(epno, priv->txstatus);
}
}
* Name: stm32_setdevaddr
****************************************************************************/
static void stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value)
{
int epno;
for (epno = 0; epno < STM32_NENDPOINTS; epno++)
{
if (stm32_epreserved(priv, epno))
{
stm32_setepaddress((uint8_t)epno, (uint8_t)epno);
}
}
stm32_putreg(value | USB_DADDR_EF, STM32_USB_DADDR);
}
* Name: stm32_ep0setup
****************************************************************************/
static void stm32_ep0setup(struct stm32_usbdev_s *priv)
{
struct stm32_ep_s *ep0 = &priv->eplist[EP0];
struct stm32_req_s *privreq = stm32_rqpeek(ep0);
struct stm32_ep_s *privep;
union wb_u value;
union wb_u index;
union wb_u len;
union wb_u response;
bool handled = false;
uint8_t epno;
int nbytes = 0;
* was a zero-length transfer!)
*/
while (!stm32_rqempty(ep0))
{
int16_t result = OK;
if (privreq->req.xfrd != privreq->req.len)
{
result = -EPROTO;
}
usbtrace(TRACE_COMPLETE(ep0->ep.eplog), privreq->req.xfrd);
stm32_reqcomplete(ep0, result);
}
ep0->stalled = 0;
ep0->txbusy = 0;
value.w = 0;
index.w = 0;
len.w = 0;
response.w = 0;
if (priv->ep0state != EP0STATE_SETUP_READY)
{
* request
*/
stm32_copyfrompma((uint8_t *)&priv->ctrl, stm32_geteprxaddr(EP0),
USB_SIZEOF_CTRLREQ);
value.w = GETUINT16(priv->ctrl.value);
index.w = GETUINT16(priv->ctrl.index);
len.w = GETUINT16(priv->ctrl.len);
uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);
if (USB_REQ_ISOUT(priv->ctrl.type) && len.w > 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUT), len.w);
priv->ep0state = EP0STATE_SETUP_OUT;
return;
}
else
{
priv->ep0state = EP0STATE_SETUP_READY;
}
}
if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_NOSTDREQ), priv->ctrl.type);
stm32_dispatchrequest(priv);
return;
}
* USB device controller driver; pass what is left to the class driver
*/
switch (priv->ctrl.req)
{
case USB_REQ_GETSTATUS:
{
* value: 0
* index: zero interface endpoint
* len: 2; data = status
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSTATUS),
priv->ctrl.type);
if (len.w != 2 || (priv->ctrl.type &
USB_REQ_DIR_IN) == 0 ||
index.b[MSB] != 0 || value.w != 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), 0);
priv->ep0state = EP0STATE_STALLED;
}
else
{
switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_ENDPOINT:
{
epno = USB_EPNO(index.b[LSB]);
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS),
epno);
if (epno >= STM32_NENDPOINTS)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS),
epno);
priv->ep0state = EP0STATE_STALLED;
}
else
{
response.w = 0;
nbytes = 2;
if (USB_ISEPIN(index.b[LSB]))
{
if (stm32_eptxstalled(epno))
{
response.b[LSB] = 1;
}
}
else
{
if (stm32_eprxstalled(epno))
{
response.b[LSB] = 1;
}
}
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
{
if (index.w == 0)
{
usbtrace(TRACE_INTDECODE(
STM32_TRACEINTID_DEVGETSTATUS), 0);
response.w = 0;
response.b[LSB] = (priv->selfpowered <<
USB_FEATURE_SELFPOWERED) |
(1 << USB_FEATURE_REMOTEWAKEUP);
nbytes = 2;
}
else
{
usbtrace(TRACE_DEVERROR(
STM32_TRACEERR_BADDEVGETSTATUS), 0);
priv->ep0state = EP0STATE_STALLED;
}
}
break;
case USB_REQ_RECIPIENT_INTERFACE:
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0);
response.w = 0;
nbytes = 2;
}
break;
default:
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSTATUS), 0);
priv->ep0state = EP0STATE_STALLED;
}
break;
}
}
}
break;
case USB_REQ_CLEARFEATURE:
{
* value: feature selector
* index: zero interface endpoint;
* len: zero, data = none
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_CLEARFEATURE),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_ENDPOINT)
{
* (except for the endpoint recipient)
*/
stm32_dispatchrequest(priv);
handled = true;
}
else
{
epno = USB_EPNO(index.b[LSB]);
if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = 0;
stm32_epstall(&privep->ep, true);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADCLEARFEATURE), 0);
priv->ep0state = EP0STATE_STALLED;
}
}
}
break;
case USB_REQ_SETFEATURE:
{
* value: feature selector
* index: zero interface endpoint;
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETFEATURE),
priv->ctrl.type);
if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE) &&
value.w == USB_FEATURE_TESTMODE)
{
uinfo("test mode: %d\n", index.w);
}
else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_ENDPOINT)
{
* recipient=endpoint
*/
stm32_dispatchrequest(priv);
handled = true;
}
else
{
epno = USB_EPNO(index.b[LSB]);
if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = 1;
stm32_epstall(&privep->ep, false);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0);
priv->ep0state = EP0STATE_STALLED;
}
}
}
break;
case USB_REQ_SETADDRESS:
{
* value: device address
* index: 0
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPSETADDRESS),
value.w);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_DEVICE ||
index.w != 0 || len.w != 0 || value.w > 127)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETADDRESS), 0);
priv->ep0state = EP0STATE_STALLED;
}
* A zero-length packet will be sent and the device address
* will be set when the zero- length packet transfer completes.
*/
}
break;
case USB_REQ_GETDESCRIPTOR:
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
case USB_REQ_SETDESCRIPTOR:
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETDESC),
priv->ctrl.type);
* let the class implementation handle it
*/
stm32_dispatchrequest(priv);
handled = true;
}
break;
case USB_REQ_GETCONFIGURATION:
* value: 0;
* index: 0;
* len: 1; data = configuration value
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETCONFIG),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE &&
value.w == 0 && index.w == 0 && len.w == 1)
{
* let the class implementation handle it
*/
stm32_dispatchrequest(priv);
handled = true;
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETCONFIG), 0);
priv->ep0state = EP0STATE_STALLED;
}
}
break;
case USB_REQ_SETCONFIGURATION:
* value: configuration value
* index: 0;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETCONFIG),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE &&
index.w == 0 && len.w == 0)
{
* let the class implementation handle it
*/
stm32_dispatchrequest(priv);
handled = true;
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETCONFIG), 0);
priv->ep0state = EP0STATE_STALLED;
}
}
break;
case USB_REQ_GETINTERFACE:
* value: 0
* index: interface;
* len: 1; data = alt interface
*/
case USB_REQ_SETINTERFACE:
* value: alternate setting
* index: interface;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETIF),
priv->ctrl.type);
stm32_dispatchrequest(priv);
handled = true;
}
break;
case USB_REQ_SYNCHFRAME:
* value: 0
* index: endpoint;
* len: 2; data = frame number
*/
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SYNCHFRAME), 0);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDCTRLREQ),
priv->ctrl.req);
priv->ep0state = EP0STATE_STALLED;
}
break;
}
* outcomes:
*
* 1. The setup request was successfully handled above and a response
* packet must be sent (may be a zero length packet).
* 2. The request was successfully handled by the class implementation.
* In case, the EP0 IN response has already been queued and the local
* variable 'handled' will be set to true and
* ep0state != EP0STATE_STALLED;
* 3. An error was detected in either the above logic or by the class
* implementation logic. In either case, priv->state will be set
* EP0STATE_STALLED to indicate this case.
*
* NOTE:
* Non-standard requests are a special case. They are handled by the
* class implementation and this function returned early above, skipping
* this logic altogether.
*/
if (priv->ep0state != EP0STATE_STALLED && !handled)
{
* requested in the setup packet
*/
if (nbytes > len.w)
{
nbytes = len.w;
}
stm32_epwrite(priv, ep0, response.b, nbytes);
priv->ep0state = EP0STATE_IDLE;
}
}
* Name: stm32_ep0in
****************************************************************************/
static void stm32_ep0in(struct stm32_usbdev_s *priv)
{
priv->eplist[EP0].txbusy = false;
* from the class driver?
*/
if (priv->ep0state == EP0STATE_WRREQUEST)
{
stm32_wrrequest_ep0(priv, &priv->eplist[EP0]);
}
else if (priv->ep0state == EP0STATE_IDLE)
{
* If so, then now is the time to set the address.
*/
if (priv->ctrl.req == USB_REQ_SETADDRESS &&
(priv->ctrl.type & REQRECIPIENT_MASK) ==
(USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_DEVICE))
{
union wb_u value;
value.w = GETUINT16(priv->ctrl.value);
stm32_setdevaddr(priv, value.b[LSB]);
}
}
else
{
priv->ep0state = EP0STATE_STALLED;
}
}
* Name: stm32_ep0out
****************************************************************************/
static void stm32_ep0out(struct stm32_usbdev_s *priv)
{
int ret;
struct stm32_ep_s *privep = &priv->eplist[EP0];
switch (priv->ep0state)
{
case EP0STATE_RDREQUEST:
case EP0STATE_IDLE:
ret = stm32_rdrequest(priv, privep);
priv->ep0state = ((ret == OK) ? EP0STATE_RDREQUEST : EP0STATE_IDLE);
break;
case EP0STATE_SETUP_OUT:
ret = stm32_ep0_rdrequest(priv);
* last set up command with the OUT
* data
*/
priv->ep0state = EP0STATE_IDLE;
* data greater then the length of
* CONFIG_USBDEV_SETUP_MAXDATASIZE
* so we are done
*/
break;
default:
* completed, STALL the endpoint in either case
*/
priv->ep0state = EP0STATE_STALLED;
break;
}
}
* Name: stm32_ep0done
****************************************************************************/
static inline void stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr)
{
uint16_t epr;
* status because the hardware is supposed to set the both RX and TX status
* to NAK when an EP0 SETUP occurs (of course, this might not be a setup)
*/
priv->rxstatus = USB_EPR_STATRX_NAK;
priv->txstatus = USB_EPR_STATTX_NAK;
stm32_seteprxstatus(EP0, USB_EPR_STATRX_NAK);
stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK);
* packet sent to or received from the host PC.
*/
if ((istr & USB_ISTR_DIR) == 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0IN), istr);
stm32_clrepctrtx(EP0);
stm32_ep0in(priv);
}
else
{
epr = stm32_getreg(STM32_USB_EPR(EP0));
* completes on an endpoint
*/
if ((epr & USB_EPR_CTR_TX) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0INDONE), epr);
stm32_clrepctrtx(EP0);
stm32_ep0in(priv);
}
* transaction was a control endpoint SETUP
*/
else if ((epr & USB_EPR_SETUP) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPDONE), epr);
stm32_clrepctrrx(EP0);
stm32_ep0setup(priv);
}
* completed on this endpoint.
*/
else if ((epr & USB_EPR_CTR_RX) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0OUTDONE), epr);
stm32_clrepctrrx(EP0);
stm32_ep0out(priv);
}
else
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0BADCTR), epr);
return;
}
}
stm32_seteprxcount(EP0, STM32_EP0MAXPACKET);
* consequences of the above EP0 operations:
*
* rxstatus txstatus ep0state MEANING
* -------- -------- --------- ---------------------------------
* NAK NAK IDLE Nothing happened
* NAK VALID IDLE EP0 response sent from USBDEV driver
* NAK VALID WRREQUEST EP0 response sent from class driver
* NAK --- STALL Some protocol error occurred
*
* First handle the STALL condition:
*/
if (priv->ep0state == EP0STATE_STALLED)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED),
priv->ep0state);
priv->rxstatus = USB_EPR_STATRX_STALL;
priv->txstatus = USB_EPR_STATTX_STALL;
}
* only special case to handle is when both are set to NAK. In that
* case, we need to set RX status to VALID in order to accept the next
* SETUP request.
*/
else if (priv->rxstatus == USB_EPR_STATRX_NAK &&
priv->txstatus == USB_EPR_STATTX_NAK)
{
priv->rxstatus = USB_EPR_STATRX_VALID;
}
stm32_seteprxstatus(EP0, priv->rxstatus);
stm32_seteptxstatus(EP0, priv->txstatus);
}
* Name: stm32_lptransfer
****************************************************************************/
static void stm32_lptransfer(struct stm32_usbdev_s *priv)
{
uint8_t epno;
uint16_t istr;
while (((istr = stm32_getreg(STM32_USB_ISTR)) & USB_ISTR_CTR) != 0)
{
stm32_putreg((uint16_t)~USB_ISTR_CTR, STM32_USB_ISTR);
epno = (uint8_t)(istr & USB_ISTR_EPID_MASK);
if (epno == 0)
{
stm32_ep0done(priv, istr);
}
else
{
stm32_epdone(priv, epno);
}
}
}
* Name: stm32_hpinterrupt
****************************************************************************/
static int stm32_hpinterrupt(int irq, void *context, void *arg)
{
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct stm32_usbdev_s *priv = &g_usbdev;
uint16_t istr;
uint8_t epno;
* for isochronous and double-buffer bulk transfers.
*/
istr = stm32_getreg(STM32_USB_ISTR);
usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_HPINTERRUPT), istr);
while ((istr & USB_ISTR_CTR) != 0)
{
stm32_putreg((uint16_t)~USB_ISTR_CTR, STM32_USB_ISTR);
epno = (uint8_t)(istr & USB_ISTR_EPID_MASK);
stm32_epdone(priv, epno);
istr = stm32_getreg(STM32_USB_ISTR);
}
usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_HPINTERRUPT), 0);
return OK;
}
* Name: stm32_lpinterrupt
****************************************************************************/
static int stm32_lpinterrupt(int irq, void *context, void *arg)
{
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct stm32_usbdev_s *priv = &g_usbdev;
uint16_t istr = stm32_getreg(STM32_USB_ISTR);
usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_LPINTERRUPT), istr);
* in the same conditions it is left by the system reset (but with the
* USB controller enabled).
*/
if ((istr & USB_ISTR_RESET) != 0)
{
stm32_putreg(~USB_ISTR_RESET, STM32_USB_ISTR);
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RESET), istr);
* valid.
*/
stm32_reset(priv);
goto exit_lpinterrupt;
}
* This interrupt is only enable while the USB is suspended.
*/
if ((istr & USB_ISTR_WKUP & priv->imask) != 0)
{
* cause of the resume is indicated in the FNR register
*/
stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR);
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WKUP),
stm32_getreg(STM32_USB_FNR));
stm32_initresume(priv);
priv->rsmstate = RSMSTATE_IDLE;
* re-enable the suspend interrupt. Clear any pending SUSP
* interrupts.
*/
stm32_setimask(priv, USB_CNTR_SUSPM, USB_CNTR_ESOFM | USB_CNTR_WKUPM);
stm32_putreg(~USB_CNTR_SUSPM, STM32_USB_ISTR);
}
if ((istr & USB_ISTR_SUSP & priv->imask) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSP), 0);
stm32_suspend(priv);
* of USB_CNTR_FSUSP
*/
stm32_putreg(~USB_ISTR_SUSP, STM32_USB_ISTR);
}
if ((istr & USB_ISTR_ESOF & priv->imask) != 0)
{
stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR);
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ESOF), 0);
stm32_esofpoll(priv);
}
if ((istr & USB_ISTR_CTR & priv->imask) != 0)
{
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_LPCTR), istr);
stm32_lptransfer(priv);
}
exit_lpinterrupt:
usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_LPINTERRUPT),
stm32_getreg(STM32_USB_EP0R));
return OK;
}
* Name: stm32_setimask
****************************************************************************/
static void
stm32_setimask(struct stm32_usbdev_s *priv,
uint16_t setbits, uint16_t clrbits)
{
uint16_t regval;
priv->imask &= ~clrbits;
priv->imask |= setbits;
* register (Hmmm... who is shadowing whom?)
*/
regval = stm32_getreg(STM32_USB_CNTR);
regval &= ~USB_CNTR_ALLINTS;
regval |= priv->imask;
stm32_putreg(regval, STM32_USB_CNTR);
}
* Suspend/Resume Helpers
****************************************************************************/
* Name: stm32_suspend
****************************************************************************/
static void stm32_suspend(struct stm32_usbdev_s *priv)
{
uint16_t regval;
if (priv->driver)
{
CLASS_SUSPEND(priv->driver, &priv->usbdev);
}
* interrupt. Clear any pending WKUP interrupt.
*/
stm32_setimask(priv, USB_CNTR_WKUPM, USB_CNTR_ESOFM | USB_CNTR_SUSPM);
stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR);
* within the USB peripheral and disables further SUSP interrupts.
*/
regval = stm32_getreg(STM32_USB_CNTR);
regval |= USB_CNTR_FSUSP;
stm32_putreg(regval, STM32_USB_CNTR);
if (!priv->selfpowered)
{
* consumption in the USB analog transceivers but keeps them
* able to detect resume activity
*/
regval = stm32_getreg(STM32_USB_CNTR);
regval |= USB_CNTR_LPMODE;
stm32_putreg(regval, STM32_USB_CNTR);
}
* state
*/
stm32_usbsuspend((struct usbdev_s *)priv, false);
}
* Name: stm32_initresume
****************************************************************************/
static void stm32_initresume(struct stm32_usbdev_s *priv)
{
uint16_t regval;
* from the host PC, or (2) the class device implementation calls the
* wakeup() method.
*/
* a self-powered device. Also, low power mode is automatically cleared by
* hardware when a WKUP interrupt event occurs).
*/
regval = stm32_getreg(STM32_USB_CNTR);
regval &= (~USB_CNTR_LPMODE);
stm32_putreg(regval, STM32_USB_CNTR);
stm32_usbsuspend((struct usbdev_s *)priv, true);
stm32_putreg(STM32_CNTR_SETUP, STM32_USB_CNTR);
if (priv->driver)
{
CLASS_RESUME(priv->driver, &priv->usbdev);
}
}
* Name: stm32_esofpoll
****************************************************************************/
static void stm32_esofpoll(struct stm32_usbdev_s *priv)
{
uint16_t regval;
switch (priv->rsmstate)
{
case RSMSTATE_STARTED:
regval = stm32_getreg(STM32_USB_CNTR);
regval |= USB_CNTR_RESUME;
stm32_putreg(regval, STM32_USB_CNTR);
priv->rsmstate = RSMSTATE_WAITING;
priv->nesofs = 10;
break;
case RSMSTATE_WAITING:
priv->nesofs--;
if (priv->nesofs == 0)
{
regval = stm32_getreg(STM32_USB_CNTR);
regval &= (~USB_CNTR_RESUME);
stm32_putreg(regval, STM32_USB_CNTR);
priv->rsmstate = RSMSTATE_IDLE;
* the WKUP interrupt. Clear any pending WKUP interrupt.
*/
stm32_setimask(priv,
USB_CNTR_WKUPM, USB_CNTR_ESOFM | USB_CNTR_SUSPM);
stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR);
}
break;
case RSMSTATE_IDLE:
default:
priv->rsmstate = RSMSTATE_IDLE;
break;
}
}
* Endpoint Helpers
****************************************************************************/
* Name: stm32_epreserve
****************************************************************************/
static inline struct stm32_ep_s *
stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset)
{
struct stm32_ep_s *privep = NULL;
irqstate_t flags;
int epndx = 0;
flags = enter_critical_section();
epset &= priv->epavail;
if (epset)
{
* (skipping EP0)
*/
for (epndx = 1; epndx < STM32_NENDPOINTS; epndx++)
{
uint8_t bit = STM32_ENDP_BIT(epndx);
if ((epset & bit) != 0)
{
priv->epavail &= ~bit;
privep = &priv->eplist[epndx];
break;
}
}
}
leave_critical_section(flags);
return privep;
}
* Name: stm32_epunreserve
****************************************************************************/
static inline void
stm32_epunreserve(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep)
{
irqstate_t flags = enter_critical_section();
priv->epavail |= STM32_ENDP_BIT(USB_EPNO(privep->ep.eplog));
leave_critical_section(flags);
}
* Name: stm32_epreserved
****************************************************************************/
static inline bool
stm32_epreserved(struct stm32_usbdev_s *priv, int epno)
{
return ((priv->epavail & STM32_ENDP_BIT(epno)) == 0);
}
* Name: stm32_epallocpma
****************************************************************************/
static int stm32_epallocpma(struct stm32_usbdev_s *priv)
{
irqstate_t flags;
int bufno = ERROR;
int bufndx;
flags = enter_critical_section();
for (bufndx = 2; bufndx < STM32_NBUFFERS; bufndx++)
{
uint8_t bit = STM32_BUFFER_BIT(bufndx);
if ((priv->bufavail & bit) != 0)
{
priv->bufavail &= ~bit;
bufno = bufndx;
break;
}
}
leave_critical_section(flags);
return bufno;
}
* Name: stm32_epfreepma
****************************************************************************/
static inline void
stm32_epfreepma(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep)
{
irqstate_t flags = enter_critical_section();
priv->epavail |= STM32_ENDP_BIT(privep->bufno);
leave_critical_section(flags);
}
* Endpoint operations
****************************************************************************/
* Name: stm32_epconfigure
****************************************************************************/
static int stm32_epconfigure(struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc,
bool last)
{
struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
uint16_t pma;
uint16_t setting;
uint16_t maxpacket;
uint8_t epno;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep || !desc)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
uerr("ERROR: ep=%p desc=%p\n", ep, desc);
return -EINVAL;
}
#endif
epno = USB_EPNO(desc->addr);
usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno);
DEBUGASSERT(epno == USB_EPNO(ep->eplog));
switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
{
case USB_EP_ATTR_XFER_INT:
setting = USB_EPR_EPTYPE_INTERRUPT;
break;
case USB_EP_ATTR_XFER_BULK:
setting = USB_EPR_EPTYPE_BULK;
break;
case USB_EP_ATTR_XFER_ISOC:
#warning "REVISIT: Need to review isochronous EP setup"
setting = USB_EPR_EPTYPE_ISOC;
break;
case USB_EP_ATTR_XFER_CONTROL:
setting = USB_EPR_EPTYPE_CONTROL;
break;
default:
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPTYPE),
(uint16_t)desc->type);
return -EINVAL;
}
stm32_seteptype(epno, setting);
#warning "REVISIT: Should configure BULK EPs using double buffer feature"
pma = STM32_BUFNO2BUF(privep->bufno);
maxpacket = GETUINT16(desc->mxpacketsize);
DEBUGASSERT(maxpacket <= STM32_MAXPACKET_SIZE);
ep->maxpacket = maxpacket;
if (USB_ISEPIN(desc->addr))
{
ep->eplog = USB_EPIN(epno);
stm32_seteptxaddr(epno, pma);
stm32_seteptxstatus(epno, USB_EPR_STATTX_NAK);
stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS);
}
else
{
ep->eplog = USB_EPOUT(epno);
stm32_seteprxaddr(epno, pma);
stm32_seteprxcount(epno, maxpacket);
stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID);
stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS);
}
stm32_dumpep(epno);
return OK;
}
* Name: stm32_epdisable
****************************************************************************/
static int stm32_epdisable(struct usbdev_ep_s *ep)
{
struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
irqstate_t flags;
uint8_t epno;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
uerr("ERROR: ep=%p\n", ep);
return -EINVAL;
}
#endif
epno = USB_EPNO(ep->eplog);
usbtrace(TRACE_EPDISABLE, epno);
flags = enter_critical_section();
stm32_cancelrequests(privep);
stm32_seteprxcount(epno, 0);
stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS);
stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS);
leave_critical_section(flags);
return OK;
}
* Name: stm32_epallocreq
****************************************************************************/
static struct usbdev_req_s *stm32_epallocreq(struct usbdev_ep_s *ep)
{
struct stm32_req_s *privreq;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return NULL;
}
#endif
usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog));
privreq = kmm_malloc(sizeof(struct stm32_req_s));
if (!privreq)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_ALLOCFAIL), 0);
return NULL;
}
memset(privreq, 0, sizeof(struct stm32_req_s));
return &privreq->req;
}
* Name: stm32_epfreereq
****************************************************************************/
static void stm32_epfreereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
struct stm32_req_s *privreq = (struct stm32_req_s *)req;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return;
}
#endif
usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog));
kmm_free(privreq);
}
* Name: stm32_epsubmit
****************************************************************************/
static int stm32_epsubmit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
struct stm32_req_s *privreq = (struct stm32_req_s *)req;
struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
struct stm32_usbdev_s *priv;
irqstate_t flags;
uint8_t epno;
int ret = OK;
#ifdef CONFIG_DEBUG_FEATURES
if (!req || !req->callback || !req->buf || !ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
uerr("ERROR: req=%p callback=%p buf=%p ep=%p\n",
req, req->callback, req->buf, ep);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog));
priv = privep->dev;
#ifdef CONFIG_DEBUG_FEATURES
if (!priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED),
priv->usbdev.speed);
uerr("ERROR: driver=%p\n", priv->driver);
return -ESHUTDOWN;
}
#endif
epno = USB_EPNO(ep->eplog);
req->result = -EINPROGRESS;
req->xfrd = 0;
flags = enter_critical_section();
if (privep->stalled)
{
stm32_abortrequest(privep, privreq, -EBUSY);
uerr("ERROR: stalled\n");
ret = -EBUSY;
}
* using the bi-directional EP0, then we assume that they intend the EP0
* IN functionality.
*/
else if (USB_ISEPIN(ep->eplog) || epno == EP0)
{
stm32_rqenqueue(privep, privreq);
usbtrace(TRACE_INREQQUEUED(epno), req->len);
if (!privep->txbusy)
{
priv->txstatus = USB_EPR_STATTX_NAK;
if (epno == EP0)
{
ret = stm32_wrrequest_ep0(priv, privep);
}
else
{
ret = stm32_wrrequest(priv, privep);
}
stm32_seteptxstatus(epno, priv->txstatus);
}
}
else
{
privep->txnullpkt = 0;
stm32_rqenqueue(privep, privreq);
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
if (priv->rxpending)
{
* transactions. "While the STAT_RX bits are equal to '10'
* (NAK), any OUT request addressed to that endpoint is NAKed,
* indicating a flow control condition: the USB host will retry
* the transaction until it succeeds."
*/
priv->rxstatus = USB_EPR_STATRX_VALID;
stm32_seteprxstatus(epno, priv->rxstatus);
priv->rxpending = false;
}
}
leave_critical_section(flags);
return ret;
}
* Name: stm32_epcancel
****************************************************************************/
static int stm32_epcancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
struct stm32_ep_s *privep = (struct stm32_ep_s *)ep;
irqstate_t flags;
#ifdef CONFIG_DEBUG_USB
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog));
flags = enter_critical_section();
stm32_cancelrequests(privep);
leave_critical_section(flags);
return OK;
}
* Name: stm32_epstall
****************************************************************************/
static int stm32_epstall(struct usbdev_ep_s *ep, bool resume)
{
struct stm32_ep_s *privep;
struct stm32_usbdev_s *priv;
uint8_t epno;
uint16_t status;
irqstate_t flags;
#ifdef CONFIG_DEBUG_USB
if (!ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
privep = (struct stm32_ep_s *)ep;
priv = (struct stm32_usbdev_s *)privep->dev;
epno = USB_EPNO(ep->eplog);
flags = enter_critical_section();
usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, USB_EPNO(ep->eplog));
* disabled
*/
if (USB_ISEPIN(ep->eplog))
{
status = stm32_geteptxstatus(epno);
}
else
{
status = stm32_geteprxstatus(epno);
}
if (status == 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPDISABLED), 0);
if (epno == 0)
{
priv->ep0state = EP0STATE_STALLED;
}
leave_critical_section(flags);
return -ENODEV;
}
if (resume)
{
usbtrace(TRACE_EPRESUME, epno);
privep->stalled = false;
if (USB_ISEPIN(ep->eplog))
{
if (stm32_eptxstalled(epno))
{
stm32_clrtxdtog(epno);
priv->txstatus = USB_EPR_STATTX_NAK;
if (epno == EP0)
{
stm32_wrrequest_ep0(priv, privep);
}
else
{
stm32_wrrequest(priv, privep);
}
stm32_seteptxstatus(epno, priv->txstatus);
}
}
else
{
if (stm32_eprxstalled(epno))
{
if (epno == EP0)
{
* receiver
*/
stm32_seteprxcount(epno, ep->maxpacket);
}
else
{
stm32_clrrxdtog(epno);
}
priv->rxstatus = USB_EPR_STATRX_VALID;
stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID);
}
}
}
else
{
usbtrace(TRACE_EPSTALL, epno);
privep->stalled = true;
if (USB_ISEPIN(ep->eplog))
{
priv->txstatus = USB_EPR_STATTX_STALL;
stm32_seteptxstatus(epno, USB_EPR_STATTX_STALL);
}
else
{
priv->rxstatus = USB_EPR_STATRX_STALL;
stm32_seteprxstatus(epno, USB_EPR_STATRX_STALL);
}
}
leave_critical_section(flags);
return OK;
}
* Device Controller Operations
****************************************************************************/
* Name: stm32_allocep
****************************************************************************/
static struct usbdev_ep_s *stm32_allocep(struct usbdev_s *dev, uint8_t epno,
bool in, uint8_t eptype)
{
struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;
struct stm32_ep_s *privep = NULL;
uint8_t epset = STM32_ENDP_ALLSET;
int bufno;
usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno);
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return NULL;
}
#endif
epno = USB_EPNO(epno);
if (epno > 0)
{
* requested 'logical' endpoint.
* All of the other checks will still be performed.
*
* First, verify that the logical endpoint is in the range supported by
* by the hardware.
*/
if (epno >= STM32_NENDPOINTS)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t)epno);
return NULL;
}
* remove all of the candidate endpoints from the bitset except for the
* the IN/OUT pair for this logical address.
*/
epset = STM32_ENDP_BIT(epno);
}
privep = stm32_epreserve(priv, epset);
if (!privep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPRESERVE), (uint16_t)epset);
goto errout;
}
#warning "REVISIT: Should configure BULK EPs using double buffer feature"
bufno = stm32_epallocpma(priv);
if (bufno < 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPBUFFER), 0);
goto errout_with_ep;
}
privep->bufno = (uint8_t)bufno;
return &privep->ep;
errout_with_ep:
stm32_epunreserve(priv, privep);
errout:
return NULL;
}
* Name: stm32_freeep
****************************************************************************/
static void stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep)
{
struct stm32_usbdev_s *priv;
struct stm32_ep_s *privep;
#ifdef CONFIG_DEBUG_USB
if (!dev || !ep)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return;
}
#endif
priv = (struct stm32_usbdev_s *)dev;
privep = (struct stm32_ep_s *)ep;
usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog));
if (priv && privep)
{
stm32_epfreepma(priv, privep);
stm32_epunreserve(priv, privep);
}
}
* Name: stm32_getframe
****************************************************************************/
static int stm32_getframe(struct usbdev_s *dev)
{
uint16_t fnr;
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
fnr = stm32_getreg(STM32_USB_FNR);
usbtrace(TRACE_DEVGETFRAME, fnr);
return (fnr & USB_FNR_FN_MASK);
}
* Name: stm32_wakeup
****************************************************************************/
static int stm32_wakeup(struct usbdev_s *dev)
{
struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;
irqstate_t flags;
usbtrace(TRACE_DEVWAKEUP, 0);
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
* by the ESOF interrupt.
*/
flags = enter_critical_section();
stm32_initresume(priv);
priv->rsmstate = RSMSTATE_STARTED;
* the WKUP interrupt (we are already waking up), and enable the
* ESOF interrupt that will drive the resume operations. Clear any
* pending ESOF interrupt.
*/
stm32_setimask(priv, USB_CNTR_ESOFM, USB_CNTR_WKUPM | USB_CNTR_SUSPM);
stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR);
leave_critical_section(flags);
return OK;
}
* Name: stm32_selfpowered
****************************************************************************/
static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;
usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -ENODEV;
}
#endif
priv->selfpowered = selfpowered;
return OK;
}
* Initialization/Reset
****************************************************************************/
* Name: stm32_reset
****************************************************************************/
static void stm32_reset(struct stm32_usbdev_s *priv)
{
int epno;
stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
* should then accept any new configurations.
*/
CLASS_DISCONNECT(priv->driver, &priv->usbdev);
priv->ep0state = EP0STATE_IDLE;
priv->rsmstate = RSMSTATE_IDLE;
priv->rxpending = false;
for (epno = 0; epno < STM32_NENDPOINTS; epno++)
{
struct stm32_ep_s *privep = &priv->eplist[epno];
* with status -ESHUTDOWN, then will not be requeued
* until the configuration is reset. NOTE: This should
* not be necessary... the CLASS_DISCONNECT above should
* result in the class implementation calling stm32_epdisable
* for each of its configured endpoints.
*/
stm32_cancelrequests(privep);
privep->stalled = false;
privep->halted = false;
privep->txbusy = false;
privep->txnullpkt = false;
}
stm32_hwreset(priv);
priv->usbdev.speed = USB_SPEED_FULL;
}
* Name: stm32_hwreset
****************************************************************************/
static void stm32_hwreset(struct stm32_usbdev_s *priv)
{
stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
priv->imask = 0;
stm32_putreg(priv->imask, STM32_USB_CNTR);
stm32_putreg(STM32_BTABLE_ADDRESS & 0xfff8, STM32_USB_BTABLE);
stm32_seteptype(EP0, USB_EPR_EPTYPE_CONTROL);
stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK);
stm32_seteprxaddr(EP0, STM32_EP0_RXADDR);
stm32_seteprxcount(EP0, STM32_EP0MAXPACKET);
stm32_seteptxaddr(EP0, STM32_EP0_TXADDR);
stm32_clrstatusout(EP0);
stm32_seteprxstatus(EP0, USB_EPR_STATRX_VALID);
stm32_setdevaddr(priv, 0);
stm32_putreg(0, STM32_USB_ISTR);
stm32_setimask(priv, STM32_CNTR_SETUP,
(USB_CNTR_ALLINTS & ~STM32_CNTR_SETUP));
stm32_dumpep(EP0);
}
* Name: stm32_hwsetup
****************************************************************************/
static void stm32_hwsetup(struct stm32_usbdev_s *priv)
{
int epno;
* all USB interrupts
*/
stm32_putreg(USB_CNTR_FRES | USB_CNTR_PDWN, STM32_USB_CNTR);
* host to enumerate us until the class driver is registered.
*/
stm32_usbpullup(&priv->usbdev, false);
* have the initial value of zero and, hence, are not explicitly
* initialized here.
*/
memset(priv, 0, sizeof(struct stm32_usbdev_s));
priv->usbdev.ops = &g_devops;
priv->usbdev.ep0 = &priv->eplist[EP0].ep;
priv->epavail = STM32_ENDP_ALLSET & ~STM32_ENDP_BIT(EP0);
priv->bufavail = STM32_BUFFER_ALLSET & ~STM32_BUFFER_EP0;
for (epno = 0; epno < STM32_NENDPOINTS; epno++)
{
* really necessary because there is only one controller), and
* the (physical) endpoint number which is just the index to the
* endpoint.
*/
priv->eplist[epno].ep.ops = &g_epops;
priv->eplist[epno].dev = priv;
priv->eplist[epno].ep.eplog = epno;
* ISOC endpoints could have larger maxpacket???). A smaller
* packet size can be selected when the endpoint is configured.
*/
priv->eplist[epno].ep.maxpacket = STM32_MAXPACKET_SIZE;
}
#if STM32_EP0MAXPACKET < STM32_MAXPACKET_SIZE
priv->eplist[EP0].ep.maxpacket = STM32_EP0MAXPACKET;
#endif
*
* PA9 - VBUS
* PA10 - ID
* PA11 - DM
* PA12 - DP
*
* "As soon as the USB is enabled, these pins [DM and DP] are connected to
* the USB internal transceiver automatically."
*/
* about 1uS after applying power before the USB will behave predictably.
* A 5MS delay is more than enough. NOTE that we leave the USB controller
* in the reset state; the hardware will not be initialized until the
* class driver has been bound.
*/
stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
up_mdelay(5);
}
* Name: stm32_hwshutdown
****************************************************************************/
static void stm32_hwshutdown(struct stm32_usbdev_s *priv)
{
priv->usbdev.speed = USB_SPEED_UNKNOWN;
stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
stm32_putreg(0, STM32_USB_ISTR);
stm32_usbpullup(&priv->usbdev, false);
stm32_putreg(USB_CNTR_FRES | USB_CNTR_PDWN, STM32_USB_CNTR);
}
* Public Functions
****************************************************************************/
* Name: arm_usbinitialize
* Description:
* Initialize the USB driver
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void arm_usbinitialize(void)
{
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct stm32_usbdev_s *priv = &g_usbdev;
usbtrace(TRACE_DEVINIT, 0);
stm32_checksetup();
#ifdef CONFIG_STM32_STM32F30XX
stm32_configgpio(GPIO_USB_DM);
stm32_configgpio(GPIO_USB_DP);
#endif
stm32_hwsetup(priv);
* (Only supported by the STM32 F3 family)
*/
#ifdef CONFIG_STM32_STM32F30XX
# ifdef CONFIG_STM32_USB_ITRMP
modifyreg32(STM32_RCC_APB1ENR, SYSCFG_CFGR1_USB_ITRMP, 0);
# else
modifyreg32(STM32_RCC_APB1ENR, 0, SYSCFG_CFGR1_USB_ITRMP);
# endif
#endif
* initialized and interrupts will not be enabled until the class device
* driver is bound. Getting the IRQs here only makes sure that we have
* them when we need them later.
*/
if (irq_attach(STM32_IRQ_USBHP, stm32_hpinterrupt, NULL) != 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION),
(uint16_t)STM32_IRQ_USBHP);
goto errout;
}
if (irq_attach(STM32_IRQ_USBLP, stm32_lpinterrupt, NULL) != 0)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION),
(uint16_t)STM32_IRQ_USBLP);
goto errout;
}
return;
errout:
arm_usbuninitialize();
}
* Name: arm_usbuninitialize
* Description:
* Initialize the USB driver
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void arm_usbuninitialize(void)
{
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct stm32_usbdev_s *priv = &g_usbdev;
irqstate_t flags;
flags = enter_critical_section();
usbtrace(TRACE_DEVUNINIT, 0);
up_disable_irq(STM32_IRQ_USBHP);
up_disable_irq(STM32_IRQ_USBLP);
irq_detach(STM32_IRQ_USBHP);
irq_detach(STM32_IRQ_USBLP);
if (priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0);
usbdev_unregister(priv->driver);
}
stm32_hwshutdown(priv);
leave_critical_section(flags);
}
* Name: usbdev_register
*
* Description:
* Register a USB device class driver. The class driver's bind() method
* will be called to bind it to a USB device driver.
*
****************************************************************************/
int usbdev_register(struct usbdevclass_driver_s *driver)
{
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct stm32_usbdev_s *priv = &g_usbdev;
int ret;
usbtrace(TRACE_DEVREGISTER, 0);
#ifdef CONFIG_DEBUG_USB
if (!driver || !driver->ops->bind || !driver->ops->unbind ||
!driver->ops->disconnect || !driver->ops->setup)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
if (priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0);
return -EBUSY;
}
#endif
priv->driver = driver;
ret = CLASS_BIND(driver, &priv->usbdev);
if (ret)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16_t) - ret);
}
else
{
* controller
*/
stm32_hwreset(priv);
up_enable_irq(STM32_IRQ_USBHP);
up_enable_irq(STM32_IRQ_USBLP);
* some time after this
*/
stm32_usbpullup(&priv->usbdev, true);
priv->usbdev.speed = USB_SPEED_FULL;
}
return ret;
}
* Name: usbdev_unregister
*
* Description:
* Un-register usbdev class driver. If the USB device is connected to a
* USB host, it will first disconnect(). The driver is also requested to
* unbind() and clean up any device state, before this procedure finally
* returns.
*
****************************************************************************/
int usbdev_unregister(struct usbdevclass_driver_s *driver)
{
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct stm32_usbdev_s *priv = &g_usbdev;
irqstate_t flags;
usbtrace(TRACE_DEVUNREGISTER, 0);
#ifdef CONFIG_DEBUG_USB
if (driver != priv->driver)
{
usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
* canceled while the class driver is still bound.
*/
flags = enter_critical_section();
stm32_reset(priv);
CLASS_UNBIND(driver, &priv->usbdev);
up_disable_irq(STM32_IRQ_USBHP);
up_disable_irq(STM32_IRQ_USBLP);
* in the reset state (this is probably not necessary, the stm32_reset()
* call above was probably sufficient).
*/
stm32_hwshutdown(priv);
stm32_hwsetup(priv);
priv->driver = NULL;
leave_critical_section(flags);
return OK;
}
#endif