Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
f6f83a6c
Commit
f6f83a6c
authored
Mar 31, 2013
by
Al Viro
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
aic79xx: switch to ->show_info()
Signed-off-by:
Al Viro
<
viro@zeniv.linux.org.uk
>
parent
887fc88e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
53 additions
and
131 deletions
+53
-131
drivers/scsi/aic7xxx/aic79xx_osm.c
drivers/scsi/aic7xxx/aic79xx_osm.c
+2
-7
drivers/scsi/aic7xxx/aic79xx_osm.h
drivers/scsi/aic7xxx/aic79xx_osm.h
+2
-10
drivers/scsi/aic7xxx/aic79xx_proc.c
drivers/scsi/aic7xxx/aic79xx_proc.c
+49
-114
No files found.
drivers/scsi/aic7xxx/aic79xx_osm.c
View file @
f6f83a6c
...
@@ -906,7 +906,8 @@ struct scsi_host_template aic79xx_driver_template = {
...
@@ -906,7 +906,8 @@ struct scsi_host_template aic79xx_driver_template = {
.
module
=
THIS_MODULE
,
.
module
=
THIS_MODULE
,
.
name
=
"aic79xx"
,
.
name
=
"aic79xx"
,
.
proc_name
=
"aic79xx"
,
.
proc_name
=
"aic79xx"
,
.
proc_info
=
ahd_linux_proc_info
,
.
show_info
=
ahd_linux_show_info
,
.
write_info
=
ahd_proc_write_seeprom
,
.
info
=
ahd_linux_info
,
.
info
=
ahd_linux_info
,
.
queuecommand
=
ahd_linux_queue
,
.
queuecommand
=
ahd_linux_queue
,
.
eh_abort_handler
=
ahd_linux_abort
,
.
eh_abort_handler
=
ahd_linux_abort
,
...
@@ -1702,19 +1703,13 @@ ahd_send_async(struct ahd_softc *ahd, char channel,
...
@@ -1702,19 +1703,13 @@ ahd_send_async(struct ahd_softc *ahd, char channel,
switch
(
code
)
{
switch
(
code
)
{
case
AC_TRANSFER_NEG
:
case
AC_TRANSFER_NEG
:
{
{
char
buf
[
80
];
struct
scsi_target
*
starget
;
struct
scsi_target
*
starget
;
struct
info_str
info
;
struct
ahd_initiator_tinfo
*
tinfo
;
struct
ahd_initiator_tinfo
*
tinfo
;
struct
ahd_tmode_tstate
*
tstate
;
struct
ahd_tmode_tstate
*
tstate
;
unsigned
int
target_ppr_options
;
unsigned
int
target_ppr_options
;
BUG_ON
(
target
==
CAM_TARGET_WILDCARD
);
BUG_ON
(
target
==
CAM_TARGET_WILDCARD
);
info
.
buffer
=
buf
;
info
.
length
=
sizeof
(
buf
);
info
.
offset
=
0
;
info
.
pos
=
0
;
tinfo
=
ahd_fetch_transinfo
(
ahd
,
channel
,
ahd
->
our_id
,
tinfo
=
ahd_fetch_transinfo
(
ahd
,
channel
,
ahd
->
our_id
,
target
,
&
tstate
);
target
,
&
tstate
);
...
...
drivers/scsi/aic7xxx/aic79xx_osm.h
View file @
f6f83a6c
...
@@ -379,14 +379,6 @@ void ahd_insb(struct ahd_softc * ahd, long port,
...
@@ -379,14 +379,6 @@ void ahd_insb(struct ahd_softc * ahd, long port,
int
ahd_linux_register_host
(
struct
ahd_softc
*
,
int
ahd_linux_register_host
(
struct
ahd_softc
*
,
struct
scsi_host_template
*
);
struct
scsi_host_template
*
);
/*************************** Pretty Printing **********************************/
struct
info_str
{
char
*
buffer
;
int
length
;
off_t
offset
;
int
pos
;
};
/******************************** Locking *************************************/
/******************************** Locking *************************************/
static
inline
void
static
inline
void
ahd_lockinit
(
struct
ahd_softc
*
ahd
)
ahd_lockinit
(
struct
ahd_softc
*
ahd
)
...
@@ -513,8 +505,8 @@ ahd_flush_device_writes(struct ahd_softc *ahd)
...
@@ -513,8 +505,8 @@ ahd_flush_device_writes(struct ahd_softc *ahd)
}
}
/**************************** Proc FS Support *********************************/
/**************************** Proc FS Support *********************************/
int
ahd_
linux_proc_info
(
struct
Scsi_Host
*
,
char
*
,
char
**
,
int
ahd_
proc_write_seeprom
(
struct
Scsi_Host
*
,
char
*
,
int
);
off_t
,
int
,
int
);
int
ahd_linux_show_info
(
struct
seq_file
*
,
struct
Scsi_Host
*
);
/*********************** Transaction Access Wrappers **************************/
/*********************** Transaction Access Wrappers **************************/
static
inline
void
ahd_cmd_set_transaction_status
(
struct
scsi_cmnd
*
,
uint32_t
);
static
inline
void
ahd_cmd_set_transaction_status
(
struct
scsi_cmnd
*
,
uint32_t
);
...
...
drivers/scsi/aic7xxx/aic79xx_proc.c
View file @
f6f83a6c
...
@@ -42,16 +42,12 @@
...
@@ -42,16 +42,12 @@
#include "aic79xx_osm.h"
#include "aic79xx_osm.h"
#include "aic79xx_inline.h"
#include "aic79xx_inline.h"
static
void
copy_mem_info
(
struct
info_str
*
info
,
char
*
data
,
int
len
);
static
int
copy_info
(
struct
info_str
*
info
,
char
*
fmt
,
...);
static
void
ahd_dump_target_state
(
struct
ahd_softc
*
ahd
,
static
void
ahd_dump_target_state
(
struct
ahd_softc
*
ahd
,
struct
info_str
*
info
,
struct
seq_file
*
m
,
u_int
our_id
,
char
channel
,
u_int
our_id
,
char
channel
,
u_int
target_id
);
u_int
target_id
);
static
void
ahd_dump_device_state
(
struct
info_str
*
info
,
static
void
ahd_dump_device_state
(
struct
seq_file
*
m
,
struct
scsi_device
*
sdev
);
struct
scsi_device
*
sdev
);
static
int
ahd_proc_write_seeprom
(
struct
ahd_softc
*
ahd
,
char
*
buffer
,
int
length
);
/*
/*
* Table of syncrates that don't follow the "divisible by 4"
* Table of syncrates that don't follow the "divisible by 4"
...
@@ -93,58 +89,15 @@ ahd_calc_syncsrate(u_int period_factor)
...
@@ -93,58 +89,15 @@ ahd_calc_syncsrate(u_int period_factor)
return
(
10000000
/
(
period_factor
*
4
*
10
));
return
(
10000000
/
(
period_factor
*
4
*
10
));
}
}
static
void
copy_mem_info
(
struct
info_str
*
info
,
char
*
data
,
int
len
)
{
if
(
info
->
pos
+
len
>
info
->
offset
+
info
->
length
)
len
=
info
->
offset
+
info
->
length
-
info
->
pos
;
if
(
info
->
pos
+
len
<
info
->
offset
)
{
info
->
pos
+=
len
;
return
;
}
if
(
info
->
pos
<
info
->
offset
)
{
off_t
partial
;
partial
=
info
->
offset
-
info
->
pos
;
data
+=
partial
;
info
->
pos
+=
partial
;
len
-=
partial
;
}
if
(
len
>
0
)
{
memcpy
(
info
->
buffer
,
data
,
len
);
info
->
pos
+=
len
;
info
->
buffer
+=
len
;
}
}
static
int
copy_info
(
struct
info_str
*
info
,
char
*
fmt
,
...)
{
va_list
args
;
char
buf
[
256
];
int
len
;
va_start
(
args
,
fmt
);
len
=
vsprintf
(
buf
,
fmt
,
args
);
va_end
(
args
);
copy_mem_info
(
info
,
buf
,
len
);
return
(
len
);
}
static
void
static
void
ahd_format_transinfo
(
struct
info_str
*
info
,
struct
ahd_transinfo
*
tinfo
)
ahd_format_transinfo
(
struct
seq_file
*
m
,
struct
ahd_transinfo
*
tinfo
)
{
{
u_int
speed
;
u_int
speed
;
u_int
freq
;
u_int
freq
;
u_int
mb
;
u_int
mb
;
if
(
tinfo
->
period
==
AHD_PERIOD_UNKNOWN
)
{
if
(
tinfo
->
period
==
AHD_PERIOD_UNKNOWN
)
{
copy_info
(
info
,
"Renegotiation Pending
\n
"
);
seq_printf
(
m
,
"Renegotiation Pending
\n
"
);
return
;
return
;
}
}
speed
=
3300
;
speed
=
3300
;
...
@@ -156,34 +109,34 @@ ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
...
@@ -156,34 +109,34 @@ ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
speed
*=
(
0x01
<<
tinfo
->
width
);
speed
*=
(
0x01
<<
tinfo
->
width
);
mb
=
speed
/
1000
;
mb
=
speed
/
1000
;
if
(
mb
>
0
)
if
(
mb
>
0
)
copy_info
(
info
,
"%d.%03dMB/s transfers"
,
mb
,
speed
%
1000
);
seq_printf
(
m
,
"%d.%03dMB/s transfers"
,
mb
,
speed
%
1000
);
else
else
copy_info
(
info
,
"%dKB/s transfers"
,
speed
);
seq_printf
(
m
,
"%dKB/s transfers"
,
speed
);
if
(
freq
!=
0
)
{
if
(
freq
!=
0
)
{
int
printed_options
;
int
printed_options
;
printed_options
=
0
;
printed_options
=
0
;
copy_info
(
info
,
" (%d.%03dMHz"
,
freq
/
1000
,
freq
%
1000
);
seq_printf
(
m
,
" (%d.%03dMHz"
,
freq
/
1000
,
freq
%
1000
);
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_RD_STRM
)
!=
0
)
{
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_RD_STRM
)
!=
0
)
{
copy_info
(
info
,
" RDSTRM"
);
seq_printf
(
m
,
" RDSTRM"
);
printed_options
++
;
printed_options
++
;
}
}
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_DT_REQ
)
!=
0
)
{
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_DT_REQ
)
!=
0
)
{
copy_info
(
info
,
"%s"
,
printed_options
?
"|DT"
:
" DT"
);
seq_printf
(
m
,
"%s"
,
printed_options
?
"|DT"
:
" DT"
);
printed_options
++
;
printed_options
++
;
}
}
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_IU_REQ
)
!=
0
)
{
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_IU_REQ
)
!=
0
)
{
copy_info
(
info
,
"%s"
,
printed_options
?
"|IU"
:
" IU"
);
seq_printf
(
m
,
"%s"
,
printed_options
?
"|IU"
:
" IU"
);
printed_options
++
;
printed_options
++
;
}
}
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_RTI
)
!=
0
)
{
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_RTI
)
!=
0
)
{
copy_info
(
info
,
"%s"
,
seq_printf
(
m
,
"%s"
,
printed_options
?
"|RTI"
:
" RTI"
);
printed_options
?
"|RTI"
:
" RTI"
);
printed_options
++
;
printed_options
++
;
}
}
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_QAS_REQ
)
!=
0
)
{
if
((
tinfo
->
ppr_options
&
MSG_EXT_PPR_QAS_REQ
)
!=
0
)
{
copy_info
(
info
,
"%s"
,
seq_printf
(
m
,
"%s"
,
printed_options
?
"|QAS"
:
" QAS"
);
printed_options
?
"|QAS"
:
" QAS"
);
printed_options
++
;
printed_options
++
;
}
}
...
@@ -191,19 +144,19 @@ ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
...
@@ -191,19 +144,19 @@ ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo)
if
(
tinfo
->
width
>
0
)
{
if
(
tinfo
->
width
>
0
)
{
if
(
freq
!=
0
)
{
if
(
freq
!=
0
)
{
copy_info
(
info
,
", "
);
seq_printf
(
m
,
", "
);
}
else
{
}
else
{
copy_info
(
info
,
" ("
);
seq_printf
(
m
,
" ("
);
}
}
copy_info
(
info
,
"%dbit)"
,
8
*
(
0x01
<<
tinfo
->
width
));
seq_printf
(
m
,
"%dbit)"
,
8
*
(
0x01
<<
tinfo
->
width
));
}
else
if
(
freq
!=
0
)
{
}
else
if
(
freq
!=
0
)
{
copy_info
(
info
,
")"
);
seq_printf
(
m
,
")"
);
}
}
copy_info
(
info
,
"
\n
"
);
seq_printf
(
m
,
"
\n
"
);
}
}
static
void
static
void
ahd_dump_target_state
(
struct
ahd_softc
*
ahd
,
struct
info_str
*
info
,
ahd_dump_target_state
(
struct
ahd_softc
*
ahd
,
struct
seq_file
*
m
,
u_int
our_id
,
char
channel
,
u_int
target_id
)
u_int
our_id
,
char
channel
,
u_int
target_id
)
{
{
struct
scsi_target
*
starget
;
struct
scsi_target
*
starget
;
...
@@ -213,17 +166,17 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
...
@@ -213,17 +166,17 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
tinfo
=
ahd_fetch_transinfo
(
ahd
,
channel
,
our_id
,
tinfo
=
ahd_fetch_transinfo
(
ahd
,
channel
,
our_id
,
target_id
,
&
tstate
);
target_id
,
&
tstate
);
copy_info
(
info
,
"Target %d Negotiation Settings
\n
"
,
target_id
);
seq_printf
(
m
,
"Target %d Negotiation Settings
\n
"
,
target_id
);
copy_info
(
info
,
"
\t
User: "
);
seq_printf
(
m
,
"
\t
User: "
);
ahd_format_transinfo
(
info
,
&
tinfo
->
user
);
ahd_format_transinfo
(
m
,
&
tinfo
->
user
);
starget
=
ahd
->
platform_data
->
starget
[
target_id
];
starget
=
ahd
->
platform_data
->
starget
[
target_id
];
if
(
starget
==
NULL
)
if
(
starget
==
NULL
)
return
;
return
;
copy_info
(
info
,
"
\t
Goal: "
);
seq_printf
(
m
,
"
\t
Goal: "
);
ahd_format_transinfo
(
info
,
&
tinfo
->
goal
);
ahd_format_transinfo
(
m
,
&
tinfo
->
goal
);
copy_info
(
info
,
"
\t
Curr: "
);
seq_printf
(
m
,
"
\t
Curr: "
);
ahd_format_transinfo
(
info
,
&
tinfo
->
curr
);
ahd_format_transinfo
(
m
,
&
tinfo
->
curr
);
for
(
lun
=
0
;
lun
<
AHD_NUM_LUNS
;
lun
++
)
{
for
(
lun
=
0
;
lun
<
AHD_NUM_LUNS
;
lun
++
)
{
struct
scsi_device
*
dev
;
struct
scsi_device
*
dev
;
...
@@ -233,29 +186,30 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
...
@@ -233,29 +186,30 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info,
if
(
dev
==
NULL
)
if
(
dev
==
NULL
)
continue
;
continue
;
ahd_dump_device_state
(
info
,
dev
);
ahd_dump_device_state
(
m
,
dev
);
}
}
}
}
static
void
static
void
ahd_dump_device_state
(
struct
info_str
*
info
,
struct
scsi_device
*
sdev
)
ahd_dump_device_state
(
struct
seq_file
*
m
,
struct
scsi_device
*
sdev
)
{
{
struct
ahd_linux_device
*
dev
=
scsi_transport_device_data
(
sdev
);
struct
ahd_linux_device
*
dev
=
scsi_transport_device_data
(
sdev
);
copy_info
(
info
,
"
\t
Channel %c Target %d Lun %d Settings
\n
"
,
seq_printf
(
m
,
"
\t
Channel %c Target %d Lun %d Settings
\n
"
,
sdev
->
sdev_target
->
channel
+
'A'
,
sdev
->
sdev_target
->
channel
+
'A'
,
sdev
->
sdev_target
->
id
,
sdev
->
lun
);
sdev
->
sdev_target
->
id
,
sdev
->
lun
);
copy_info
(
info
,
"
\t\t
Commands Queued %ld
\n
"
,
dev
->
commands_issued
);
seq_printf
(
m
,
"
\t\t
Commands Queued %ld
\n
"
,
dev
->
commands_issued
);
copy_info
(
info
,
"
\t\t
Commands Active %d
\n
"
,
dev
->
active
);
seq_printf
(
m
,
"
\t\t
Commands Active %d
\n
"
,
dev
->
active
);
copy_info
(
info
,
"
\t\t
Command Openings %d
\n
"
,
dev
->
openings
);
seq_printf
(
m
,
"
\t\t
Command Openings %d
\n
"
,
dev
->
openings
);
copy_info
(
info
,
"
\t\t
Max Tagged Openings %d
\n
"
,
dev
->
maxtags
);
seq_printf
(
m
,
"
\t\t
Max Tagged Openings %d
\n
"
,
dev
->
maxtags
);
copy_info
(
info
,
"
\t\t
Device Queue Frozen Count %d
\n
"
,
dev
->
qfrozen
);
seq_printf
(
m
,
"
\t\t
Device Queue Frozen Count %d
\n
"
,
dev
->
qfrozen
);
}
}
static
int
int
ahd_proc_write_seeprom
(
struct
ahd_softc
*
ahd
,
char
*
buffer
,
int
length
)
ahd_proc_write_seeprom
(
struct
Scsi_Host
*
shost
,
char
*
buffer
,
int
length
)
{
{
struct
ahd_softc
*
ahd
=
*
(
struct
ahd_softc
**
)
shost
->
hostdata
;
ahd_mode_state
saved_modes
;
ahd_mode_state
saved_modes
;
int
have_seeprom
;
int
have_seeprom
;
u_long
s
;
u_long
s
;
...
@@ -319,64 +273,45 @@ ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length)
...
@@ -319,64 +273,45 @@ ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length)
* Return information to handle /proc support for the driver.
* Return information to handle /proc support for the driver.
*/
*/
int
int
ahd_linux_proc_info
(
struct
Scsi_Host
*
shost
,
char
*
buffer
,
char
**
start
,
ahd_linux_show_info
(
struct
seq_file
*
m
,
struct
Scsi_Host
*
shost
)
off_t
offset
,
int
length
,
int
inout
)
{
{
struct
ahd_softc
*
ahd
=
*
(
struct
ahd_softc
**
)
shost
->
hostdata
;
struct
ahd_softc
*
ahd
=
*
(
struct
ahd_softc
**
)
shost
->
hostdata
;
struct
info_str
info
;
char
ahd_info
[
256
];
char
ahd_info
[
256
];
u_int
max_targ
;
u_int
max_targ
;
u_int
i
;
u_int
i
;
int
retval
;
/* Has data been written to the file? */
seq_printf
(
m
,
"Adaptec AIC79xx driver version: %s
\n
"
,
if
(
inout
==
TRUE
)
{
retval
=
ahd_proc_write_seeprom
(
ahd
,
buffer
,
length
);
goto
done
;
}
if
(
start
)
*
start
=
buffer
;
info
.
buffer
=
buffer
;
info
.
length
=
length
;
info
.
offset
=
offset
;
info
.
pos
=
0
;
copy_info
(
&
info
,
"Adaptec AIC79xx driver version: %s
\n
"
,
AIC79XX_DRIVER_VERSION
);
AIC79XX_DRIVER_VERSION
);
copy_info
(
&
info
,
"%s
\n
"
,
ahd
->
description
);
seq_printf
(
m
,
"%s
\n
"
,
ahd
->
description
);
ahd_controller_info
(
ahd
,
ahd_info
);
ahd_controller_info
(
ahd
,
ahd_info
);
copy_info
(
&
info
,
"%s
\n
"
,
ahd_info
);
seq_printf
(
m
,
"%s
\n
"
,
ahd_info
);
copy_info
(
&
info
,
"Allocated SCBs: %d, SG List Length: %d
\n\n
"
,
seq_printf
(
m
,
"Allocated SCBs: %d, SG List Length: %d
\n\n
"
,
ahd
->
scb_data
.
numscbs
,
AHD_NSEG
);
ahd
->
scb_data
.
numscbs
,
AHD_NSEG
);
max_targ
=
16
;
max_targ
=
16
;
if
(
ahd
->
seep_config
==
NULL
)
if
(
ahd
->
seep_config
==
NULL
)
copy_info
(
&
info
,
"No Serial EEPROM
\n
"
);
seq_printf
(
m
,
"No Serial EEPROM
\n
"
);
else
{
else
{
copy_info
(
&
info
,
"Serial EEPROM:
\n
"
);
seq_printf
(
m
,
"Serial EEPROM:
\n
"
);
for
(
i
=
0
;
i
<
sizeof
(
*
ahd
->
seep_config
)
/
2
;
i
++
)
{
for
(
i
=
0
;
i
<
sizeof
(
*
ahd
->
seep_config
)
/
2
;
i
++
)
{
if
(((
i
%
8
)
==
0
)
&&
(
i
!=
0
))
{
if
(((
i
%
8
)
==
0
)
&&
(
i
!=
0
))
{
copy_info
(
&
info
,
"
\n
"
);
seq_printf
(
m
,
"
\n
"
);
}
}
copy_info
(
&
info
,
"0x%.4x "
,
seq_printf
(
m
,
"0x%.4x "
,
((
uint16_t
*
)
ahd
->
seep_config
)[
i
]);
((
uint16_t
*
)
ahd
->
seep_config
)[
i
]);
}
}
copy_info
(
&
info
,
"
\n
"
);
seq_printf
(
m
,
"
\n
"
);
}
}
copy_info
(
&
info
,
"
\n
"
);
seq_printf
(
m
,
"
\n
"
);
if
((
ahd
->
features
&
AHD_WIDE
)
==
0
)
if
((
ahd
->
features
&
AHD_WIDE
)
==
0
)
max_targ
=
8
;
max_targ
=
8
;
for
(
i
=
0
;
i
<
max_targ
;
i
++
)
{
for
(
i
=
0
;
i
<
max_targ
;
i
++
)
{
ahd_dump_target_state
(
ahd
,
&
info
,
ahd
->
our_id
,
'A'
,
ahd_dump_target_state
(
ahd
,
m
,
ahd
->
our_id
,
'A'
,
/*target_id*/
i
);
/*target_id*/
i
);
}
}
retval
=
info
.
pos
>
info
.
offset
?
info
.
pos
-
info
.
offset
:
0
;
return
0
;
done:
return
(
retval
);
}
}
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