mirror of
https://github.com/apache/nuttx.git
synced 2026-06-03 22:20:31 +08:00
STM32 F1 USB: Fix OUT SETUP command bug. From David Sidrane
This commit is contained in:
@@ -5992,3 +5992,6 @@
|
|||||||
the STM32F429. From Ken Pettit (2013-11-7).
|
the STM32F429. From Ken Pettit (2013-11-7).
|
||||||
* configs/stm32f429i-disco: Support for the STM32F429I-Discovery
|
* configs/stm32f429i-disco: Support for the STM32F429I-Discovery
|
||||||
board from Ken Pettit (2013-11-7).
|
board from Ken Pettit (2013-11-7).
|
||||||
|
* arch/arm/src/stm32/stm32_usbdev.c: The long outstanding bug
|
||||||
|
involviing the handling of OUT SETUP commands has been fixed in
|
||||||
|
the STM32 F1 USB device driver by David Sidrane (2013-11-7).
|
||||||
|
|||||||
@@ -961,6 +961,10 @@ o USB (drivers/usbdev, drivers/usbhost)
|
|||||||
correctly supports OUT SETUP data following the same design as
|
correctly supports OUT SETUP data following the same design as
|
||||||
per above.
|
per above.
|
||||||
|
|
||||||
|
Update 2013-11-7: David Sidrane has fixed with issue with the
|
||||||
|
STM32 F1 USB device driver. Still a few more to go before this
|
||||||
|
can be closed out.
|
||||||
|
|
||||||
Status: Open
|
Status: Open
|
||||||
Priority: High for class drivers that need EP0 data. For example, the
|
Priority: High for class drivers that need EP0 data. For example, the
|
||||||
CDC/ACM serial driver might need the line coding data (that
|
CDC/ACM serial driver might need the line coding data (that
|
||||||
|
|||||||
@@ -77,6 +77,10 @@
|
|||||||
# define CONFIG_USBDEV_EP0_MAXSIZE 64
|
# define CONFIG_USBDEV_EP0_MAXSIZE 64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE
|
||||||
|
# define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_USB_PRI
|
#ifndef CONFIG_USB_PRI
|
||||||
# define CONFIG_USB_PRI NVIC_SYSH_PRIORITY_DEFAULT
|
# define CONFIG_USB_PRI NVIC_SYSH_PRIORITY_DEFAULT
|
||||||
#endif
|
#endif
|
||||||
@@ -240,6 +244,8 @@
|
|||||||
#define STM32_TRACEINTID_SUSP 0x001c
|
#define STM32_TRACEINTID_SUSP 0x001c
|
||||||
#define STM32_TRACEINTID_SYNCHFRAME 0x001d
|
#define STM32_TRACEINTID_SYNCHFRAME 0x001d
|
||||||
#define STM32_TRACEINTID_WKUP 0x001e
|
#define STM32_TRACEINTID_WKUP 0x001e
|
||||||
|
#define STM32_TRACEINTID_EP0SETUPOUT 0x001f
|
||||||
|
#define STM32_TRACEINTID_EP0SETUPOUTDATA 0x0020
|
||||||
|
|
||||||
/* Ever-present MIN and MAX macros */
|
/* Ever-present MIN and MAX macros */
|
||||||
|
|
||||||
@@ -270,8 +276,11 @@
|
|||||||
enum stm32_ep0state_e
|
enum stm32_ep0state_e
|
||||||
{
|
{
|
||||||
EP0STATE_IDLE = 0, /* No request in progress */
|
EP0STATE_IDLE = 0, /* No request in progress */
|
||||||
EP0STATE_RDREQUEST, /* Read request in progress */
|
EP0STATE_SETUP_OUT, /* Set up recived with data for device OUT in progress */
|
||||||
|
EP0STATE_SETUP_READY, /* Set up was recived prior and is in ctrl,
|
||||||
|
* now the data has arrived */
|
||||||
EP0STATE_WRREQUEST, /* Write request in progress */
|
EP0STATE_WRREQUEST, /* Write request in progress */
|
||||||
|
EP0STATE_RDREQUEST, /* Read request in progress */
|
||||||
EP0STATE_STALLED /* We are stalled */
|
EP0STATE_STALLED /* We are stalled */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -294,8 +303,8 @@ union wb_u
|
|||||||
|
|
||||||
struct stm32_req_s
|
struct stm32_req_s
|
||||||
{
|
{
|
||||||
struct usbdev_req_s req; /* Standard USB request */
|
struct usbdev_req_s req; /* Standard USB request */
|
||||||
struct stm32_req_s *flink; /* Supports a singly linked list */
|
struct stm32_req_s *flink; /* Supports a singly linked list */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This is the internal representation of an endpoint */
|
/* This is the internal representation of an endpoint */
|
||||||
@@ -307,7 +316,7 @@ struct stm32_ep_s
|
|||||||
* to struct stm32_ep_s.
|
* to struct stm32_ep_s.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct usbdev_ep_s ep; /* Standard endpoint structure */
|
struct usbdev_ep_s ep; /* Standard endpoint structure */
|
||||||
|
|
||||||
/* STR71X-specific fields */
|
/* STR71X-specific fields */
|
||||||
|
|
||||||
@@ -336,21 +345,41 @@ struct stm32_usbdev_s
|
|||||||
|
|
||||||
/* STM32-specific fields */
|
/* STM32-specific fields */
|
||||||
|
|
||||||
struct usb_ctrlreq_s ctrl; /* Last EP0 request */
|
uint8_t ep0state; /* State of EP0 (see enum stm32_ep0state_e) */
|
||||||
uint8_t ep0state; /* State of EP0 (see enum stm32_ep0state_e) */
|
uint8_t rsmstate; /* Resume state (see enum stm32_rsmstate_e) */
|
||||||
uint8_t rsmstate; /* Resume state (see enum stm32_rsmstate_e) */
|
uint8_t nesofs; /* ESOF counter (for resume support) */
|
||||||
uint8_t nesofs; /* ESOF counter (for resume support) */
|
uint8_t rxpending:1; /* 1: OUT data in PMA, but no read requests */
|
||||||
uint8_t rxpending:1; /* 1: OUT data in PMA, but no read requests */
|
uint8_t selfpowered:1; /* 1: Device is self powered */
|
||||||
uint8_t selfpowered:1; /* 1: Device is self powered */
|
uint8_t epavail; /* Bitset of available endpoints */
|
||||||
uint8_t epavail; /* Bitset of available endpoints */
|
uint8_t bufavail; /* Bitset of available buffers */
|
||||||
uint8_t bufavail; /* Bitset of available buffers */
|
uint16_t rxstatus; /* Saved during interrupt processing */
|
||||||
uint16_t rxstatus; /* Saved during interrupt processing */
|
uint16_t txstatus; /* " " " " " " " " */
|
||||||
uint16_t txstatus; /* " " " " " " " " */
|
uint16_t imask; /* Current interrupt mask */
|
||||||
uint16_t imask; /* Current interrupt mask */
|
|
||||||
|
/* E0 SETUP data buffering.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* Lenght of OUT DATA received in ep0data[]
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct usb_ctrlreq_s ctrl; /* Last EP0 request */
|
||||||
|
|
||||||
|
uint8_t ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE];
|
||||||
|
uint16_t ep0datlen;
|
||||||
|
|
||||||
/* The endpoint list */
|
/* The endpoint list */
|
||||||
|
|
||||||
struct stm32_ep_s eplist[STM32_NENDPOINTS];
|
struct stm32_ep_s eplist[STM32_NENDPOINTS];
|
||||||
};
|
};
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@@ -438,6 +467,8 @@ static int stm32_wrrequest(struct stm32_usbdev_s *priv,
|
|||||||
inline static int
|
inline static int
|
||||||
stm32_wrrequest_ep0(struct stm32_usbdev_s *priv,
|
stm32_wrrequest_ep0(struct stm32_usbdev_s *priv,
|
||||||
struct stm32_ep_s *privep);
|
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,
|
static int stm32_rdrequest(struct stm32_usbdev_s *priv,
|
||||||
struct stm32_ep_s *privep);
|
struct stm32_ep_s *privep);
|
||||||
static void stm32_cancelrequests(struct stm32_ep_s *privep);
|
static void stm32_cancelrequests(struct stm32_ep_s *privep);
|
||||||
@@ -570,6 +601,8 @@ const struct trace_msg_t g_usb_trace_strings_intdecode[] =
|
|||||||
TRACE_STR(STM32_TRACEINTID_SUSP ),
|
TRACE_STR(STM32_TRACEINTID_SUSP ),
|
||||||
TRACE_STR(STM32_TRACEINTID_SYNCHFRAME ),
|
TRACE_STR(STM32_TRACEINTID_SYNCHFRAME ),
|
||||||
TRACE_STR(STM32_TRACEINTID_WKUP ),
|
TRACE_STR(STM32_TRACEINTID_WKUP ),
|
||||||
|
TRACE_STR(STM32_TRACEINTID_EP0SETUPOUT ),
|
||||||
|
TRACE_STR(STM32_TRACEINTID_EP0SETUPOUTDATA ),
|
||||||
TRACE_STR_END
|
TRACE_STR_END
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@@ -577,33 +610,33 @@ const struct trace_msg_t g_usb_trace_strings_intdecode[] =
|
|||||||
#ifdef CONFIG_USBDEV_TRACE_STRINGS
|
#ifdef CONFIG_USBDEV_TRACE_STRINGS
|
||||||
const struct trace_msg_t g_usb_trace_strings_deverror[] =
|
const struct trace_msg_t g_usb_trace_strings_deverror[] =
|
||||||
{
|
{
|
||||||
TRACE_STR(STM32_TRACEERR_ALLOCFAIL ),
|
TRACE_STR(STM32_TRACEERR_ALLOCFAIL ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADCLEARFEATURE ),
|
TRACE_STR(STM32_TRACEERR_BADCLEARFEATURE ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADDEVGETSTATUS ),
|
TRACE_STR(STM32_TRACEERR_BADDEVGETSTATUS ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADEPGETSTATUS ),
|
TRACE_STR(STM32_TRACEERR_BADEPGETSTATUS ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADEPNO ),
|
TRACE_STR(STM32_TRACEERR_BADEPNO ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADEPTYPE ),
|
TRACE_STR(STM32_TRACEERR_BADEPTYPE ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADGETCONFIG ),
|
TRACE_STR(STM32_TRACEERR_BADGETCONFIG ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADGETSETDESC ),
|
TRACE_STR(STM32_TRACEERR_BADGETSETDESC ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADGETSTATUS ),
|
TRACE_STR(STM32_TRACEERR_BADGETSTATUS ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADSETADDRESS ),
|
TRACE_STR(STM32_TRACEERR_BADSETADDRESS ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADSETCONFIG ),
|
TRACE_STR(STM32_TRACEERR_BADSETCONFIG ),
|
||||||
TRACE_STR(STM32_TRACEERR_BADSETFEATURE ),
|
TRACE_STR(STM32_TRACEERR_BADSETFEATURE ),
|
||||||
TRACE_STR(STM32_TRACEERR_BINDFAILED ),
|
TRACE_STR(STM32_TRACEERR_BINDFAILED ),
|
||||||
TRACE_STR(STM32_TRACEERR_DISPATCHSTALL ),
|
TRACE_STR(STM32_TRACEERR_DISPATCHSTALL ),
|
||||||
TRACE_STR(STM32_TRACEERR_DRIVER ),
|
TRACE_STR(STM32_TRACEERR_DRIVER ),
|
||||||
TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED),
|
TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED ),
|
||||||
TRACE_STR(STM32_TRACEERR_EP0BADCTR ),
|
TRACE_STR(STM32_TRACEERR_EP0BADCTR ),
|
||||||
TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED ),
|
TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED ),
|
||||||
TRACE_STR(STM32_TRACEERR_EPBUFFER ),
|
TRACE_STR(STM32_TRACEERR_EPBUFFER ),
|
||||||
TRACE_STR(STM32_TRACEERR_EPDISABLED ),
|
TRACE_STR(STM32_TRACEERR_EPDISABLED ),
|
||||||
TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET ),
|
TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET ),
|
||||||
TRACE_STR(STM32_TRACEERR_EPRESERVE ),
|
TRACE_STR(STM32_TRACEERR_EPRESERVE ),
|
||||||
TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ ),
|
TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ ),
|
||||||
TRACE_STR(STM32_TRACEERR_INVALIDPARMS ),
|
TRACE_STR(STM32_TRACEERR_INVALIDPARMS ),
|
||||||
TRACE_STR(STM32_TRACEERR_IRQREGISTRATION ),
|
TRACE_STR(STM32_TRACEERR_IRQREGISTRATION ),
|
||||||
TRACE_STR(STM32_TRACEERR_NOTCONFIGURED ),
|
TRACE_STR(STM32_TRACEERR_NOTCONFIGURED ),
|
||||||
TRACE_STR(STM32_TRACEERR_REQABORTED ),
|
TRACE_STR(STM32_TRACEERR_REQABORTED ),
|
||||||
TRACE_STR_END
|
TRACE_STR_END
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@@ -1404,6 +1437,50 @@ static int stm32_wrrequest(struct stm32_usbdev_s *priv, struct stm32_ep_s *prive
|
|||||||
return 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 uppon 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;
|
||||||
|
|
||||||
|
/* Get the number of bytes to read from packet memory */
|
||||||
|
|
||||||
|
pmalen = stm32_geteprxcount(EP0);
|
||||||
|
|
||||||
|
ullvdbg("EP0: pmalen=%d\n", pmalen);
|
||||||
|
usbtrace(TRACE_READ(EP0), pmalen);
|
||||||
|
|
||||||
|
/* Read the data into our special buffer for SETUP data */
|
||||||
|
|
||||||
|
readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, pmalen);
|
||||||
|
src = stm32_geteprxaddr(EP0);
|
||||||
|
|
||||||
|
/* Receive the next packet */
|
||||||
|
|
||||||
|
stm32_copyfrompma(&priv->ep0data[0], src, readlen);
|
||||||
|
|
||||||
|
/* Now we can process the setup command */
|
||||||
|
|
||||||
|
priv->ep0state = EP0STATE_SETUP_READY;
|
||||||
|
priv->ep0datlen = readlen;
|
||||||
|
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUTDATA), readlen);
|
||||||
|
|
||||||
|
stm32_ep0setup(priv);
|
||||||
|
priv->ep0datlen = 0; /* mark the date consumed */
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: stm32_rdrequest
|
* Name: stm32_rdrequest
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -1505,7 +1582,8 @@ static void stm32_dispatchrequest(struct stm32_usbdev_s *priv)
|
|||||||
{
|
{
|
||||||
/* Forward to the control request to the class driver implementation */
|
/* Forward to the control request to the class driver implementation */
|
||||||
|
|
||||||
ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl, NULL, 0);
|
ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl,
|
||||||
|
priv->ep0data, priv->ep0datlen);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
/* Stall on failure */
|
/* Stall on failure */
|
||||||
@@ -1671,20 +1749,50 @@ static void stm32_ep0setup(struct stm32_usbdev_s *priv)
|
|||||||
ep0->stalled = 0;
|
ep0->stalled = 0;
|
||||||
ep0->txbusy = 0;
|
ep0->txbusy = 0;
|
||||||
|
|
||||||
/* Get a 32-bit PMA address and use that to get the 8-byte setup request */
|
/* Check to see if called from the DATA phase of a SETUP Trasfer */
|
||||||
|
|
||||||
stm32_copyfrompma((uint8_t*)&priv->ctrl, stm32_geteprxaddr(EP0), USB_SIZEOF_CTRLREQ);
|
if (priv->ep0state != EP0STATE_SETUP_READY)
|
||||||
|
{
|
||||||
|
/* Not the data phase */
|
||||||
|
/* Get a 32-bit PMA address and use that to get the 8-byte setup request */
|
||||||
|
|
||||||
/* And extract the little-endian 16-bit values to host order */
|
stm32_copyfrompma((uint8_t*)&priv->ctrl, stm32_geteprxaddr(EP0), USB_SIZEOF_CTRLREQ);
|
||||||
|
|
||||||
value.w = GETUINT16(priv->ctrl.value);
|
/* And extract the little-endian 16-bit values to host order */
|
||||||
index.w = GETUINT16(priv->ctrl.index);
|
|
||||||
len.w = GETUINT16(priv->ctrl.len);
|
|
||||||
|
|
||||||
ullvdbg("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
|
value.w = GETUINT16(priv->ctrl.value);
|
||||||
priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);
|
index.w = GETUINT16(priv->ctrl.index);
|
||||||
|
len.w = GETUINT16(priv->ctrl.len);
|
||||||
|
|
||||||
priv->ep0state = EP0STATE_IDLE;
|
ullvdbg("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
|
||||||
|
priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);
|
||||||
|
|
||||||
|
/* Is this an setup with OUT and data of length > 0 */
|
||||||
|
|
||||||
|
if (USB_REQ_ISOUT(priv->ctrl.type) && len.w > 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUT), len.w);
|
||||||
|
|
||||||
|
/* At this point priv->ctrl is the setup packet. Just ACK the
|
||||||
|
* setup packet So we can get the data.
|
||||||
|
*
|
||||||
|
* REVISIT: I suspect that this should not be here?
|
||||||
|
*/
|
||||||
|
|
||||||
|
stm32_epwrite(priv, ep0, response.b, 0);
|
||||||
|
|
||||||
|
/* Enable and Wait for the data phase. */
|
||||||
|
|
||||||
|
priv->rxstatus = USB_EPR_STATRX_VALID;
|
||||||
|
priv->ep0state = EP0STATE_SETUP_OUT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
priv->ep0state = EP0STATE_SETUP_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Dispatch any non-standard requests */
|
/* Dispatch any non-standard requests */
|
||||||
|
|
||||||
@@ -2120,12 +2228,24 @@ static void stm32_ep0out(struct stm32_usbdev_s *priv)
|
|||||||
struct stm32_ep_s *privep = &priv->eplist[EP0];
|
struct stm32_ep_s *privep = &priv->eplist[EP0];
|
||||||
switch (priv->ep0state)
|
switch (priv->ep0state)
|
||||||
{
|
{
|
||||||
case EP0STATE_RDREQUEST: /* Read request in progress */
|
case EP0STATE_RDREQUEST: /* Read request in progress */
|
||||||
case EP0STATE_IDLE: /* No transfer in progress */
|
case EP0STATE_IDLE: /* No transfer in progress */
|
||||||
ret = stm32_rdrequest(priv, privep);
|
ret = stm32_rdrequest(priv, privep);
|
||||||
priv->ep0state = ((ret == OK) ? EP0STATE_RDREQUEST : EP0STATE_IDLE);
|
priv->ep0state = ((ret == OK) ? EP0STATE_RDREQUEST : EP0STATE_IDLE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EP0STATE_SETUP_OUT: /* SETUP was waiting for data */
|
||||||
|
ret = stm32_ep0_rdrequest(priv); /* Off load the data and run the
|
||||||
|
* last set up command with the OUT
|
||||||
|
* data
|
||||||
|
*/
|
||||||
|
priv->ep0state = EP0STATE_IDLE; /* There is no notion of reciving OUT
|
||||||
|
* data greater then the length of
|
||||||
|
* CONFIG_USBDEV_SETUP_MAXDATASIZE
|
||||||
|
* so we are done
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* Unexpected state OR host aborted the OUT transfer before it
|
/* Unexpected state OR host aborted the OUT transfer before it
|
||||||
* completed, STALL the endpoint in either case
|
* completed, STALL the endpoint in either case
|
||||||
|
|||||||
Reference in New Issue
Block a user