Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
circuitpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
circuitpython
Commits
980b3317
Commit
980b3317
authored
Aug 31, 2017
by
Damien George
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
stm32/usbdev: Put all CDC state in a struct.
parent
ede8a023
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
229 additions
and
255 deletions
+229
-255
ports/stm32/usb.c
ports/stm32/usb.c
+33
-19
ports/stm32/usbd_cdc_interface.c
ports/stm32/usbd_cdc_interface.c
+71
-131
ports/stm32/usbd_cdc_interface.h
ports/stm32/usbd_cdc_interface.h
+66
-45
ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
+12
-8
ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c
ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c
+20
-25
ports/stm32/usbdev/class/src/usbd_msc_bot.c
ports/stm32/usbdev/class/src/usbd_msc_bot.c
+10
-10
ports/stm32/usbdev/class/src/usbd_msc_scsi.c
ports/stm32/usbdev/class/src/usbd_msc_scsi.c
+17
-17
No files found.
ports/stm32/usb.c
View file @
980b3317
...
...
@@ -55,6 +55,8 @@ mp_uint_t pyb_usb_flags = 0;
#ifdef USE_DEVICE_MODE
STATIC
USBD_HandleTypeDef
hUSBDDevice
;
STATIC
usbd_cdc_msc_hid_state_t
usbd_cdc_msc_hid_state
;
STATIC
usbd_cdc_itf_t
usbd_cdc_itf
;
pyb_usb_storage_medium_t
pyb_usb_storage_medium
=
PYB_USB_STORAGE_MEDIUM_NONE
;
#endif
...
...
@@ -105,25 +107,37 @@ bool pyb_usb_dev_init(uint16_t vid, uint16_t pid, usb_device_mode_t mode, USBD_H
#ifdef USE_DEVICE_MODE
if
(
!
(
pyb_usb_flags
&
PYB_USB_FLAG_DEV_ENABLED
))
{
// only init USB once in the device's power-lifetime
// configure the VID, PID and the USBD mode (interfaces it will expose)
USBD_SetVIDPIDRelease
(
vid
,
pid
,
0x0200
,
mode
==
USBD_MODE_CDC
);
if
(
USBD_SelectMode
(
mode
,
hid_info
)
!=
0
)
{
return
false
;
}
USBD_Init
(
&
hUSBDDevice
,
(
USBD_DescriptorsTypeDef
*
)
&
USBD_Descriptors
,
USB_PHY_ID
);
USBD_RegisterClass
(
&
hUSBDDevice
,
&
USBD_CDC_MSC_HID
);
USBD_CDC_RegisterInterface
(
&
hUSBDDevice
,
(
USBD_CDC_ItfTypeDef
*
)
&
USBD_CDC_fops
);
// set up the USBD state
USBD_HandleTypeDef
*
usbd
=
&
hUSBDDevice
;
usbd
->
id
=
USB_PHY_ID
;
usbd
->
dev_state
=
USBD_STATE_DEFAULT
;
usbd
->
pDesc
=
(
USBD_DescriptorsTypeDef
*
)
&
USBD_Descriptors
;
usbd
->
pClass
=
&
USBD_CDC_MSC_HID
;
usbd_cdc_msc_hid_state
.
cdc
=
&
usbd_cdc_itf
;
usbd
->
pClassData
=
&
usbd_cdc_msc_hid_state
;
switch
(
pyb_usb_storage_medium
)
{
#if MICROPY_HW_HAS_SDCARD
case
PYB_USB_STORAGE_MEDIUM_SDCARD
:
USBD_MSC_RegisterStorage
(
&
hUSBDDevice
,
(
USBD_StorageTypeDef
*
)
&
USBD_SDCARD_STORAGE_fops
);
USBD_MSC_RegisterStorage
(
usbd
,
(
USBD_StorageTypeDef
*
)
&
USBD_SDCARD_STORAGE_fops
);
break
;
#endif
default:
USBD_MSC_RegisterStorage
(
&
hUSBDDevice
,
(
USBD_StorageTypeDef
*
)
&
USBD_FLASH_STORAGE_fops
);
USBD_MSC_RegisterStorage
(
usbd
,
(
USBD_StorageTypeDef
*
)
&
USBD_FLASH_STORAGE_fops
);
break
;
}
USBD_HID_RegisterInterface
(
&
hUSBDDevice
,
(
USBD_HID_ItfTypeDef
*
)
&
USBD_HID_fops
);
USBD_Start
(
&
hUSBDDevice
);
USBD_HID_RegisterInterface
(
usbd
,
(
USBD_HID_ItfTypeDef
*
)
&
USBD_HID_fops
);
// start the USB device
USBD_LL_Init
(
usbd
);
USBD_LL_Start
(
usbd
);
}
pyb_usb_flags
|=
PYB_USB_FLAG_DEV_ENABLED
;
#endif
...
...
@@ -143,13 +157,13 @@ bool usb_vcp_is_enabled(void) {
}
int
usb_vcp_recv_byte
(
uint8_t
*
c
)
{
return
USBD_CDC_Rx
(
c
,
1
,
0
);
return
usbd_cdc_rx
(
&
usbd_cdc_itf
,
c
,
1
,
0
);
}
void
usb_vcp_send_strn
(
const
char
*
str
,
int
len
)
{
#ifdef USE_DEVICE_MODE
if
(
pyb_usb_flags
&
PYB_USB_FLAG_DEV_ENABLED
)
{
USBD_CDC_TxAlways
(
(
const
uint8_t
*
)
str
,
len
);
usbd_cdc_tx_always
(
&
usbd_cdc_itf
,
(
const
uint8_t
*
)
str
,
len
);
}
#endif
}
...
...
@@ -159,9 +173,9 @@ void usb_vcp_send_strn_cooked(const char *str, int len) {
if
(
pyb_usb_flags
&
PYB_USB_FLAG_DEV_ENABLED
)
{
for
(
const
char
*
top
=
str
+
len
;
str
<
top
;
str
++
)
{
if
(
*
str
==
'\n'
)
{
USBD_CDC_TxAlways
(
(
const
uint8_t
*
)
"
\r\n
"
,
2
);
usbd_cdc_tx_always
(
&
usbd_cdc_itf
,
(
const
uint8_t
*
)
"
\r\n
"
,
2
);
}
else
{
USBD_CDC_TxAlways
(
(
const
uint8_t
*
)
str
,
1
);
usbd_cdc_tx_always
(
&
usbd_cdc_itf
,
(
const
uint8_t
*
)
str
,
1
);
}
}
}
...
...
@@ -362,7 +376,7 @@ STATIC mp_obj_t pyb_usb_vcp_setinterrupt(mp_obj_t self_in, mp_obj_t int_chr_in)
STATIC
MP_DEFINE_CONST_FUN_OBJ_2
(
pyb_usb_vcp_setinterrupt_obj
,
pyb_usb_vcp_setinterrupt
);
STATIC
mp_obj_t
pyb_usb_vcp_isconnected
(
mp_obj_t
self_in
)
{
return
mp_obj_new_bool
(
USBD_CDC_IsConnected
(
));
return
mp_obj_new_bool
(
usbd_cdc_is_connected
(
&
usbd_cdc_itf
));
}
STATIC
MP_DEFINE_CONST_FUN_OBJ_1
(
pyb_usb_vcp_isconnected_obj
,
pyb_usb_vcp_isconnected
);
...
...
@@ -375,7 +389,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(pyb_have_cdc_obj, pyb_have_cdc);
/// \method any()
/// Return `True` if any characters waiting, else `False`.
STATIC
mp_obj_t
pyb_usb_vcp_any
(
mp_obj_t
self_in
)
{
if
(
USBD_CDC_RxNum
(
)
>
0
)
{
if
(
usbd_cdc_rx_num
(
&
usbd_cdc_itf
)
>
0
)
{
return
mp_const_true
;
}
else
{
return
mp_const_false
;
...
...
@@ -407,7 +421,7 @@ STATIC mp_obj_t pyb_usb_vcp_send(size_t n_args, const mp_obj_t *args, mp_map_t *
pyb_buf_get_for_send
(
vals
[
0
].
u_obj
,
&
bufinfo
,
data
);
// send the data
int
ret
=
USBD_CDC_Tx
(
bufinfo
.
buf
,
bufinfo
.
len
,
vals
[
1
].
u_int
);
int
ret
=
usbd_cdc_tx
(
&
usbd_cdc_itf
,
bufinfo
.
buf
,
bufinfo
.
len
,
vals
[
1
].
u_int
);
return
mp_obj_new_int
(
ret
);
}
...
...
@@ -433,7 +447,7 @@ STATIC mp_obj_t pyb_usb_vcp_recv(size_t n_args, const mp_obj_t *args, mp_map_t *
mp_obj_t
o_ret
=
pyb_buf_get_for_recv
(
vals
[
0
].
u_obj
,
&
vstr
);
// receive the data
int
ret
=
USBD_CDC_Rx
(
(
uint8_t
*
)
vstr
.
buf
,
vstr
.
len
,
vals
[
1
].
u_int
);
int
ret
=
usbd_cdc_rx
(
&
usbd_cdc_itf
,
(
uint8_t
*
)
vstr
.
buf
,
vstr
.
len
,
vals
[
1
].
u_int
);
// return the received data
if
(
o_ret
!=
MP_OBJ_NULL
)
{
...
...
@@ -470,7 +484,7 @@ STATIC const mp_rom_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
STATIC
MP_DEFINE_CONST_DICT
(
pyb_usb_vcp_locals_dict
,
pyb_usb_vcp_locals_dict_table
);
STATIC
mp_uint_t
pyb_usb_vcp_read
(
mp_obj_t
self_in
,
void
*
buf
,
mp_uint_t
size
,
int
*
errcode
)
{
int
ret
=
USBD_CDC_Rx
(
(
byte
*
)
buf
,
size
,
0
);
int
ret
=
usbd_cdc_rx
(
&
usbd_cdc_itf
,
(
byte
*
)
buf
,
size
,
0
);
if
(
ret
==
0
)
{
// return EAGAIN error to indicate non-blocking
*
errcode
=
MP_EAGAIN
;
...
...
@@ -480,7 +494,7 @@ STATIC mp_uint_t pyb_usb_vcp_read(mp_obj_t self_in, void *buf, mp_uint_t size, i
}
STATIC
mp_uint_t
pyb_usb_vcp_write
(
mp_obj_t
self_in
,
const
void
*
buf
,
mp_uint_t
size
,
int
*
errcode
)
{
int
ret
=
USBD_CDC_Tx
(
(
const
byte
*
)
buf
,
size
,
0
);
int
ret
=
usbd_cdc_tx
(
&
usbd_cdc_itf
,
(
const
byte
*
)
buf
,
size
,
0
);
if
(
ret
==
0
)
{
// return EAGAIN error to indicate non-blocking
*
errcode
=
MP_EAGAIN
;
...
...
@@ -494,10 +508,10 @@ STATIC mp_uint_t pyb_usb_vcp_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_
if
(
request
==
MP_STREAM_POLL
)
{
mp_uint_t
flags
=
arg
;
ret
=
0
;
if
((
flags
&
MP_STREAM_POLL_RD
)
&&
USBD_CDC_RxNum
(
)
>
0
)
{
if
((
flags
&
MP_STREAM_POLL_RD
)
&&
usbd_cdc_rx_num
(
&
usbd_cdc_itf
)
>
0
)
{
ret
|=
MP_STREAM_POLL_RD
;
}
if
((
flags
&
MP_STREAM_POLL_WR
)
&&
USBD_CDC_TxHalfEmpty
(
))
{
if
((
flags
&
MP_STREAM_POLL_WR
)
&&
usbd_cdc_tx_half_empty
(
&
usbd_cdc_itf
))
{
ret
|=
MP_STREAM_POLL_WR
;
}
}
else
{
...
...
ports/stm32/usbd_cdc_interface.c
View file @
980b3317
This diff is collapsed.
Click to expand it.
ports/stm32/usbd_cdc_interface.h
View file @
980b3317
/*
* This file is part of the MicroPython project, http://micropython.org/
*/
#ifndef MICROPY_INCLUDED_STMHAL_USBD_CDC_INTERFACE_H
#define MICROPY_INCLUDED_STMHAL_USBD_CDC_INTERFACE_H
/**
******************************************************************************
* @file USB_Device/CDC_Standalone/Inc/usbd_cdc_interface.h
* @author MCD Application Team
* @version V1.0.1
* @date 26-February-2014
* @brief Header for usbd_cdc_interface.c file.
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
*
* Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2
*
* 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.
*
******************************************************************************
*/
extern
const
USBD_CDC_ItfTypeDef
USBD_CDC_fops
;
int
USBD_CDC_IsConnected
(
void
);
int
USBD_CDC_TxHalfEmpty
(
void
);
int
USBD_CDC_Tx
(
const
uint8_t
*
buf
,
uint32_t
len
,
uint32_t
timeout
);
void
USBD_CDC_TxAlways
(
const
uint8_t
*
buf
,
uint32_t
len
);
int
USBD_CDC_RxNum
(
void
);
int
USBD_CDC_Rx
(
uint8_t
*
buf
,
uint32_t
len
,
uint32_t
timeout
);
#endif // MICROPY_INCLUDED_STMHAL_USBD_CDC_INTERFACE_H
/*
* This file is part of the MicroPython project, http://micropython.org/
*/
#ifndef MICROPY_INCLUDED_STMHAL_USBD_CDC_INTERFACE_H
#define MICROPY_INCLUDED_STMHAL_USBD_CDC_INTERFACE_H
/**
******************************************************************************
* @file USB_Device/CDC_Standalone/Inc/usbd_cdc_interface.h
* @author MCD Application Team
* @version V1.0.1
* @date 26-February-2014
* @brief Header for usbd_cdc_interface.c file.
******************************************************************************
* @attention
*
* <h2><center>© COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
*
* Licensed under MCD-ST Liberty SW License Agreement V2, (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.st.com/software_license_agreement_liberty_v2
*
* 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.
*
******************************************************************************
*/
#define USBD_CDC_RX_DATA_SIZE (1024) // this must be 2 or greater, and a power of 2
#define USBD_CDC_TX_DATA_SIZE (1024) // I think this can be any value (was 2048)
typedef
struct
_usbd_cdc_itf_t
{
USBD_HandleTypeDef
*
usb
;
// the parent USB device
uint8_t
rx_packet_buf
[
CDC_DATA_FS_MAX_PACKET_SIZE
];
// received data from USB OUT endpoint is stored in this buffer
uint8_t
rx_user_buf
[
USBD_CDC_RX_DATA_SIZE
];
// received data is buffered here until the user reads it
volatile
uint16_t
rx_buf_put
;
// circular buffer index
uint16_t
rx_buf_get
;
// circular buffer index
uint8_t
tx_buf
[
USBD_CDC_TX_DATA_SIZE
];
// data for USB IN endpoind is stored in this buffer
uint16_t
tx_buf_ptr_in
;
// increment this pointer modulo APP_TX_DATA_SIZE when new data is available
volatile
uint16_t
tx_buf_ptr_out
;
// increment this pointer modulo APP_TX_DATA_SIZE when data is drained
uint16_t
tx_buf_ptr_out_shadow
;
// shadow of above
uint8_t
tx_buf_ptr_wait_count
;
// used to implement a timeout waiting for low-level USB driver
uint8_t
tx_need_empty_packet
;
// used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size
volatile
uint8_t
dev_is_connected
;
// indicates if we are connected
}
usbd_cdc_itf_t
;
static
inline
int
usbd_cdc_is_connected
(
usbd_cdc_itf_t
*
cdc
)
{
return
cdc
->
dev_is_connected
;
}
int
usbd_cdc_tx_half_empty
(
usbd_cdc_itf_t
*
cdc
);
int
usbd_cdc_tx
(
usbd_cdc_itf_t
*
cdc
,
const
uint8_t
*
buf
,
uint32_t
len
,
uint32_t
timeout
);
void
usbd_cdc_tx_always
(
usbd_cdc_itf_t
*
cdc
,
const
uint8_t
*
buf
,
uint32_t
len
);
int
usbd_cdc_rx_num
(
usbd_cdc_itf_t
*
cdc
);
int
usbd_cdc_rx
(
usbd_cdc_itf_t
*
cdc
,
uint8_t
*
buf
,
uint32_t
len
,
uint32_t
timeout
);
#endif // MICROPY_INCLUDED_STMHAL_USBD_CDC_INTERFACE_H
ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
View file @
980b3317
...
...
@@ -27,13 +27,6 @@ typedef struct {
uint8_t
datatype
;
}
USBD_CDC_LineCodingTypeDef
;
typedef
struct
_USBD_CDC_Itf
{
int8_t
(
*
Init
)
(
USBD_HandleTypeDef
*
pdev
);
int8_t
(
*
DeInit
)
(
void
);
int8_t
(
*
Control
)
(
uint8_t
,
uint8_t
*
,
uint16_t
);
int8_t
(
*
Receive
)
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
*
,
uint32_t
*
);
}
USBD_CDC_ItfTypeDef
;
typedef
struct
{
uint32_t
data
[
CDC_DATA_FS_MAX_PACKET_SIZE
/
4
];
/* Force 32bits alignment */
uint8_t
CmdOpCode
;
...
...
@@ -86,6 +79,12 @@ typedef struct {
uint32_t
scsi_blk_len
;
}
USBD_MSC_BOT_HandleTypeDef
;
typedef
struct
_usbd_cdc_msc_hid_state_t
{
void
*
cdc
;
void
*
msc
;
void
*
hid
;
}
usbd_cdc_msc_hid_state_t
;
#define USBD_HID_MOUSE_MAX_PACKET (4)
#define USBD_HID_MOUSE_REPORT_DESC_SIZE (74)
...
...
@@ -103,7 +102,6 @@ int USBD_SelectMode(uint32_t mode, USBD_HID_ModeInfoTypeDef *hid_info);
// returns the current usb mode
uint8_t
USBD_GetMode
();
uint8_t
USBD_CDC_RegisterInterface
(
USBD_HandleTypeDef
*
pdev
,
USBD_CDC_ItfTypeDef
*
fops
);
uint8_t
USBD_CDC_SetTxBuffer
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
*
pbuff
,
uint16_t
length
);
uint8_t
USBD_CDC_SetRxBuffer
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
*
pbuff
);
uint8_t
USBD_CDC_ReceivePacket
(
USBD_HandleTypeDef
*
pdev
);
...
...
@@ -119,4 +117,10 @@ uint8_t USBD_HID_SendReport(USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t
uint8_t
USBD_HID_SetNAK
(
USBD_HandleTypeDef
*
pdev
);
uint8_t
USBD_HID_ClearNAK
(
USBD_HandleTypeDef
*
pdev
);
// These are provided externally to implement the CDC interface
struct
_usbd_cdc_itf_t
;
void
usbd_cdc_init
(
struct
_usbd_cdc_itf_t
*
cdc
,
USBD_HandleTypeDef
*
pdev
);
int8_t
usbd_cdc_control
(
struct
_usbd_cdc_itf_t
*
cdc
,
uint8_t
cmd
,
uint8_t
*
pbuf
,
uint16_t
length
);
int8_t
usbd_cdc_receive
(
struct
_usbd_cdc_itf_t
*
cdc
,
uint8_t
*
Buf
,
uint32_t
*
Len
);
#endif // _USB_CDC_MSC_CORE_H_
ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c
View file @
980b3317
...
...
@@ -95,7 +95,6 @@ static uint8_t usbd_config_desc_size;
static
uint8_t
*
hid_desc
;
static
const
uint8_t
*
hid_report_desc
;
static
USBD_CDC_ItfTypeDef
*
CDC_fops
;
static
USBD_StorageTypeDef
*
MSC_fops
;
static
USBD_HID_ItfTypeDef
*
HID_fops
;
...
...
@@ -647,6 +646,8 @@ static uint8_t USBD_CDC_MSC_HID_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
return
1
;
}
usbd_cdc_msc_hid_state_t
*
state
=
pdev
->
pClassData
;
if
(
usbd_mode
&
USBD_MODE_CDC
)
{
// CDC VCP component
...
...
@@ -669,7 +670,7 @@ static uint8_t USBD_CDC_MSC_HID_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
CDC_CMD_PACKET_SIZE
);
// Init physical Interface components
CDC_fops
->
Init
(
pdev
);
usbd_cdc_init
(
state
->
cdc
,
pdev
);
// Init Xfer states
CDC_ClassData
.
TxState
=
0
;
...
...
@@ -694,8 +695,8 @@ static uint8_t USBD_CDC_MSC_HID_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
USBD_EP_TYPE_BULK
,
MSC_MAX_PACKET
);
//
MSC uses the pClassData pointer because SCSI and BOT
reference it
pdev
->
pClassData
=
&
MSC_BOT_ClassData
;
//
Set the MSC data for SCSI and BOT to
reference it
state
->
msc
=
&
MSC_BOT_ClassData
;
// Init the BOT layer
MSC_BOT_Init
(
pdev
);
...
...
@@ -736,19 +737,18 @@ static uint8_t USBD_CDC_MSC_HID_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
}
static
uint8_t
USBD_CDC_MSC_HID_DeInit
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
cfgidx
)
{
if
(
usbd_mode
&
USBD_MODE_CDC
)
{
usbd_cdc_msc_hid_state_t
*
state
=
pdev
->
pClassData
;
if
((
usbd_mode
&
USBD_MODE_CDC
)
&&
state
->
cdc
)
{
// CDC VCP component
// close endpoints
USBD_LL_CloseEP
(
pdev
,
CDC_IN_EP
);
USBD_LL_CloseEP
(
pdev
,
CDC_OUT_EP
);
USBD_LL_CloseEP
(
pdev
,
CDC_CMD_EP
);
// DeInit physical Interface components
CDC_fops
->
DeInit
();
}
if
(
usbd_mode
&
USBD_MODE_MSC
)
{
if
(
(
usbd_mode
&
USBD_MODE_MSC
)
&&
state
->
msc
)
{
// MSC component
// close endpoints
...
...
@@ -758,8 +758,8 @@ static uint8_t USBD_CDC_MSC_HID_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
// DeInit the BOT layer
MSC_BOT_DeInit
(
pdev
);
// clear the pointer
pdev
->
pClassData
=
NULL
;
// clear the
state
pointer
state
->
msc
=
NULL
;
}
if
(
usbd_mode
&
USBD_MODE_HID
)
{
...
...
@@ -791,6 +791,8 @@ static uint8_t USBD_CDC_MSC_HID_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTyp
SU: 21 20 0 1
*/
usbd_cdc_msc_hid_state_t
*
state
=
pdev
->
pClassData
;
switch
(
req
->
bmRequest
&
USB_REQ_TYPE_MASK
)
{
// Class request
...
...
@@ -801,7 +803,7 @@ static uint8_t USBD_CDC_MSC_HID_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTyp
if
(
req
->
wLength
)
{
if
(
req
->
bmRequest
&
0x80
)
{
// device-to-host request
CDC_fops
->
Control
(
req
->
bRequest
,
(
uint8_t
*
)
CDC_ClassData
.
data
,
req
->
wLength
);
usbd_cdc_control
(
state
->
cdc
,
req
->
bRequest
,
(
uint8_t
*
)
CDC_ClassData
.
data
,
req
->
wLength
);
USBD_CtlSendData
(
pdev
,
(
uint8_t
*
)
CDC_ClassData
.
data
,
req
->
wLength
);
}
else
{
// host-to-device request
...
...
@@ -812,7 +814,7 @@ static uint8_t USBD_CDC_MSC_HID_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTyp
}
else
{
// Not a Data request
// Transfer the command to the interface layer
return
CDC_fops
->
Control
(
req
->
bRequest
,
NULL
,
req
->
wValue
);
return
usbd_cdc_control
(
state
->
cdc
,
req
->
bRequest
,
NULL
,
req
->
wValue
);
}
}
else
if
((
usbd_mode
&
USBD_MODE_MSC
)
&&
req
->
wIndex
==
MSC_IFACE_NUM_WITH_CDC
)
{
// MSC component
...
...
@@ -931,8 +933,9 @@ static uint8_t EP0_TxSent(USBD_HandleTypeDef *pdev) {
*/
static
uint8_t
USBD_CDC_MSC_HID_EP0_RxReady
(
USBD_HandleTypeDef
*
pdev
)
{
if
((
CDC_fops
!=
NULL
)
&&
(
CDC_ClassData
.
CmdOpCode
!=
0xff
))
{
CDC_fops
->
Control
(
CDC_ClassData
.
CmdOpCode
,
(
uint8_t
*
)
CDC_ClassData
.
data
,
CDC_ClassData
.
CmdLength
);
usbd_cdc_msc_hid_state_t
*
state
=
pdev
->
pClassData
;
if
(
state
->
cdc
!=
NULL
&&
CDC_ClassData
.
CmdOpCode
!=
0xff
)
{
usbd_cdc_control
(
state
->
cdc
,
CDC_ClassData
.
CmdOpCode
,
(
uint8_t
*
)
CDC_ClassData
.
data
,
CDC_ClassData
.
CmdLength
);
CDC_ClassData
.
CmdOpCode
=
0xff
;
}
...
...
@@ -957,13 +960,14 @@ static uint8_t USBD_CDC_MSC_HID_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
}
static
uint8_t
USBD_CDC_MSC_HID_DataOut
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
epnum
)
{
usbd_cdc_msc_hid_state_t
*
state
=
pdev
->
pClassData
;
if
((
usbd_mode
&
USBD_MODE_CDC
)
&&
epnum
==
(
CDC_OUT_EP
&
0x7f
))
{
/* Get the received data length */
CDC_ClassData
.
RxLength
=
USBD_LL_GetRxDataSize
(
pdev
,
epnum
);
/* USB data will be immediately processed, this allow next USB traffic being
NAKed till the end of the application Xfer */
CDC_fops
->
Receive
(
pdev
,
CDC_ClassData
.
RxBuffer
,
&
CDC_ClassData
.
RxLength
);
usbd_cdc_receive
(
state
->
cdc
,
CDC_ClassData
.
RxBuffer
,
&
CDC_ClassData
.
RxLength
);
return
USBD_OK
;
}
else
if
((
usbd_mode
&
USBD_MODE_MSC
)
&&
epnum
==
(
MSC_OUT_EP
&
0x7f
))
{
...
...
@@ -992,15 +996,6 @@ uint8_t *USBD_CDC_MSC_HID_GetDeviceQualifierDescriptor (uint16_t *length) {
return
NULL
;
}
uint8_t
USBD_CDC_RegisterInterface
(
USBD_HandleTypeDef
*
pdev
,
USBD_CDC_ItfTypeDef
*
fops
)
{
if
(
fops
==
NULL
)
{
return
USBD_FAIL
;
}
else
{
CDC_fops
=
fops
;
return
USBD_OK
;
}
}
uint8_t
USBD_CDC_SetTxBuffer
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
*
pbuff
,
uint16_t
length
)
{
CDC_ClassData
.
TxBuffer
=
pbuff
;
CDC_ClassData
.
TxLength
=
length
;
...
...
ports/stm32/usbdev/class/src/usbd_msc_bot.c
View file @
980b3317
...
...
@@ -104,7 +104,7 @@ static void MSC_BOT_Abort(USBD_HandleTypeDef *pdev);
*/
void
MSC_BOT_Init
(
USBD_HandleTypeDef
*
pdev
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
bot_state
=
USBD_BOT_IDLE
;
hmsc
->
bot_status
=
USBD_BOT_STATUS_NORMAL
;
...
...
@@ -132,7 +132,7 @@ void MSC_BOT_Init (USBD_HandleTypeDef *pdev)
*/
void
MSC_BOT_Reset
(
USBD_HandleTypeDef
*
pdev
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
bot_state
=
USBD_BOT_IDLE
;
hmsc
->
bot_status
=
USBD_BOT_STATUS_RECOVERY
;
...
...
@@ -152,7 +152,7 @@ void MSC_BOT_Reset (USBD_HandleTypeDef *pdev)
*/
void
MSC_BOT_DeInit
(
USBD_HandleTypeDef
*
pdev
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
bot_state
=
USBD_BOT_IDLE
;
}
...
...
@@ -166,7 +166,7 @@ void MSC_BOT_DeInit (USBD_HandleTypeDef *pdev)
void
MSC_BOT_DataIn
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
epnum
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
switch
(
hmsc
->
bot_state
)
{
...
...
@@ -199,7 +199,7 @@ void MSC_BOT_DataIn (USBD_HandleTypeDef *pdev,
void
MSC_BOT_DataOut
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
epnum
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
switch
(
hmsc
->
bot_state
)
{
...
...
@@ -231,7 +231,7 @@ void MSC_BOT_DataOut (USBD_HandleTypeDef *pdev,
*/
static
void
MSC_BOT_CBW_Decode
(
USBD_HandleTypeDef
*
pdev
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
csw
.
dTag
=
hmsc
->
cbw
.
dTag
;
hmsc
->
csw
.
dDataResidue
=
hmsc
->
cbw
.
dDataLength
;
...
...
@@ -300,7 +300,7 @@ static void MSC_BOT_SendData(USBD_HandleTypeDef *pdev,
uint8_t
*
buf
,
uint16_t
len
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
len
=
MIN
(
hmsc
->
cbw
.
dDataLength
,
len
);
hmsc
->
csw
.
dDataResidue
-=
len
;
...
...
@@ -320,7 +320,7 @@ static void MSC_BOT_SendData(USBD_HandleTypeDef *pdev,
void
MSC_BOT_SendCSW
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
CSW_Status
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
csw
.
dSignature
=
USBD_BOT_CSW_SIGNATURE
;
hmsc
->
csw
.
bStatus
=
CSW_Status
;
...
...
@@ -348,7 +348,7 @@ void MSC_BOT_SendCSW (USBD_HandleTypeDef *pdev,
static
void
MSC_BOT_Abort
(
USBD_HandleTypeDef
*
pdev
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
((
hmsc
->
cbw
.
bmFlags
==
0
)
&&
(
hmsc
->
cbw
.
dDataLength
!=
0
)
&&
...
...
@@ -377,7 +377,7 @@ static void MSC_BOT_Abort (USBD_HandleTypeDef *pdev)
void
MSC_BOT_CplClrFeature
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
epnum
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
(
hmsc
->
bot_status
==
USBD_BOT_STATUS_ERROR
)
/* Bad CBW Signature */
{
...
...
ports/stm32/usbdev/class/src/usbd_msc_scsi.c
View file @
980b3317
...
...
@@ -190,7 +190,7 @@ int8_t SCSI_ProcessCmd(USBD_HandleTypeDef *pdev,
*/
static
int8_t
SCSI_TestUnitReady
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
/* case 9 : Hi > D0 */
if
(
hmsc
->
cbw
.
dDataLength
!=
0
)
...
...
@@ -227,7 +227,7 @@ static int8_t SCSI_Inquiry(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *par
{
uint8_t
*
pPage
;
uint16_t
len
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
(
params
[
1
]
&
0x01
)
/*Evpd is set*/
{
...
...
@@ -264,7 +264,7 @@ static int8_t SCSI_Inquiry(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *par
*/
static
int8_t
SCSI_ReadCapacity10
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
(((
USBD_StorageTypeDef
*
)
pdev
->
pUserData
)
->
GetCapacity
(
lun
,
&
hmsc
->
scsi_blk_nbr
,
&
hmsc
->
scsi_blk_size
)
!=
0
)
{
...
...
@@ -300,7 +300,7 @@ static int8_t SCSI_ReadCapacity10(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_
*/
static
int8_t
SCSI_ReadFormatCapacity
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
uint16_t
blk_size
;
uint32_t
blk_nbr
;
...
...
@@ -345,7 +345,7 @@ static int8_t SCSI_ReadFormatCapacity(USBD_HandleTypeDef *pdev, uint8_t lun, ui
*/
static
int8_t
SCSI_ModeSense6
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
uint16_t
len
=
8
;
hmsc
->
bot_data_length
=
len
;
...
...
@@ -367,7 +367,7 @@ static int8_t SCSI_ModeSense6 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *
static
int8_t
SCSI_ModeSense10
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
uint16_t
len
=
8
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
bot_data_length
=
len
;
...
...
@@ -381,7 +381,7 @@ static int8_t SCSI_ModeSense10 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t
static
int8_t
SCSI_SynchronizeCache
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
// nothing to synchronize, so just return "success"
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
bot_data_length
=
0
;
return
0
;
}
...
...
@@ -397,7 +397,7 @@ static int8_t SCSI_SynchronizeCache(USBD_HandleTypeDef *pdev, uint8_t lun, uint
static
int8_t
SCSI_RequestSense
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
uint8_t
i
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
for
(
i
=
0
;
i
<
REQUEST_SENSE_DATA_LEN
;
i
++
)
{
...
...
@@ -439,7 +439,7 @@ static int8_t SCSI_RequestSense (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t
*/
void
SCSI_SenseCode
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
sKey
,
uint8_t
ASC
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
scsi_sense
[
hmsc
->
scsi_sense_tail
].
Skey
=
sKey
;
hmsc
->
scsi_sense
[
hmsc
->
scsi_sense_tail
].
w
.
ASC
=
ASC
<<
8
;
...
...
@@ -458,7 +458,7 @@ void SCSI_SenseCode(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t sKey, uint8_
*/
static
int8_t
SCSI_StartStopUnit
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
bot_data_length
=
0
;
// On Mac OS X, when the device is ejected a SCSI_START_STOP_UNIT command is sent.
...
...
@@ -479,7 +479,7 @@ static int8_t SCSI_StartStopUnit(USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t
*/
static
int8_t
SCSI_AllowMediumRemoval
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
hmsc
->
bot_data_length
=
0
;
((
USBD_StorageTypeDef
*
)
pdev
->
pUserData
)
->
PreventAllowMediumRemoval
(
lun
,
params
[
0
]);
return
0
;
...
...
@@ -494,7 +494,7 @@ static int8_t SCSI_AllowMediumRemoval(USBD_HandleTypeDef *pdev, uint8_t lun, ui
*/
static
int8_t
SCSI_Read10
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
(
hmsc
->
bot_state
==
USBD_BOT_IDLE
)
/* Idle */
{
...
...
@@ -562,7 +562,7 @@ static int8_t SCSI_Read10(USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t *para
static
int8_t
SCSI_Write10
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
(
hmsc
->
bot_state
==
USBD_BOT_IDLE
)
/* Idle */
{
...
...
@@ -652,7 +652,7 @@ static int8_t SCSI_Write10 (USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t *pa
static
int8_t
SCSI_Verify10
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint8_t
*
params
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
((
params
[
1
]
&
0x02
)
==
0x02
)
{
...
...
@@ -687,7 +687,7 @@ static int8_t SCSI_Verify10(USBD_HandleTypeDef *pdev, uint8_t lun , uint8_t *pa
*/
static
int8_t
SCSI_CheckAddressRange
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
,
uint32_t
blk_offset
,
uint16_t
blk_nbr
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
if
((
blk_offset
+
blk_nbr
)
>
hmsc
->
scsi_blk_nbr
)
{
...
...
@@ -708,7 +708,7 @@ static int8_t SCSI_CheckAddressRange (USBD_HandleTypeDef *pdev, uint8_t lun , u
*/
static
int8_t
SCSI_ProcessRead
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
)
{
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
uint32_t
len
;
len
=
MIN
(
hmsc
->
scsi_blk_len
,
MSC_MEDIA_PACKET
);
...
...
@@ -756,7 +756,7 @@ static int8_t SCSI_ProcessRead (USBD_HandleTypeDef *pdev, uint8_t lun)
static
int8_t
SCSI_ProcessWrite
(
USBD_HandleTypeDef
*
pdev
,
uint8_t
lun
)
{
uint32_t
len
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
pdev
->
pClassData
;
USBD_MSC_BOT_HandleTypeDef
*
hmsc
=
((
usbd_cdc_msc_hid_state_t
*
)
pdev
->
pClassData
)
->
msc
;
len
=
MIN
(
hmsc
->
scsi_blk_len
,
MSC_MEDIA_PACKET
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment