Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci
Commits
aa58329f
Commit
aa58329f
authored
Oct 22, 2008
by
Len Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'acer-wmi' into test
parents
6b3c4f8b
ae3a1b46
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
177 additions
and
106 deletions
+177
-106
Documentation/laptops/acer-wmi.txt
Documentation/laptops/acer-wmi.txt
+4
-24
drivers/acpi/wmi.c
drivers/acpi/wmi.c
+37
-2
drivers/misc/Kconfig
drivers/misc/Kconfig
+1
-0
drivers/misc/acer-wmi.c
drivers/misc/acer-wmi.c
+135
-80
No files found.
Documentation/laptops/acer-wmi.txt
View file @
aa58329f
Acer Laptop WMI Extras Driver
Acer Laptop WMI Extras Driver
http://code.google.com/p/aceracpi
http://code.google.com/p/aceracpi
Version 0.
1
Version 0.
2
9th February
2008
18th August
2008
Copyright 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
Copyright 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
...
@@ -87,17 +87,7 @@ acer-wmi come with built-in wireless. However, should you feel so inclined to
...
@@ -87,17 +87,7 @@ acer-wmi come with built-in wireless. However, should you feel so inclined to
ever wish to remove the card, or swap it out at some point, please get in touch
ever wish to remove the card, or swap it out at some point, please get in touch
with me, as we may well be able to gain some data on wireless card detection.
with me, as we may well be able to gain some data on wireless card detection.
To read the status of the wireless radio (0=off, 1=on):
The wireless radio is exposed through rfkill.
cat /sys/devices/platform/acer-wmi/wireless
To enable the wireless radio:
echo 1 > /sys/devices/platform/acer-wmi/wireless
To disable the wireless radio:
echo 0 > /sys/devices/platform/acer-wmi/wireless
To set the state of the wireless radio when loading acer-wmi, pass:
wireless=X (where X is 0 or 1)
Bluetooth
Bluetooth
*********
*********
...
@@ -117,17 +107,7 @@ For the adventurously minded - if you want to buy an internal bluetooth
...
@@ -117,17 +107,7 @@ For the adventurously minded - if you want to buy an internal bluetooth
module off the internet that is compatible with your laptop and fit it, then
module off the internet that is compatible with your laptop and fit it, then
it will work just fine with acer-wmi.
it will work just fine with acer-wmi.
To read the status of the bluetooth module (0=off, 1=on):
Bluetooth is exposed through rfkill.
cat /sys/devices/platform/acer-wmi/wireless
To enable the bluetooth module:
echo 1 > /sys/devices/platform/acer-wmi/bluetooth
To disable the bluetooth module:
echo 0 > /sys/devices/platform/acer-wmi/bluetooth
To set the state of the bluetooth module when loading acer-wmi, pass:
bluetooth=X (where X is 0 or 1)
3G
3G
**
**
...
...
drivers/acpi/wmi.c
View file @
aa58329f
...
@@ -217,6 +217,35 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
...
@@ -217,6 +217,35 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
return
0
;
return
0
;
}
}
static
acpi_status
wmi_method_enable
(
struct
wmi_block
*
wblock
,
int
enable
)
{
struct
guid_block
*
block
=
NULL
;
char
method
[
5
];
struct
acpi_object_list
input
;
union
acpi_object
params
[
1
];
acpi_status
status
;
acpi_handle
handle
;
block
=
&
wblock
->
gblock
;
handle
=
wblock
->
handle
;
if
(
!
block
)
return
AE_NOT_EXIST
;
input
.
count
=
1
;
input
.
pointer
=
params
;
params
[
0
].
type
=
ACPI_TYPE_INTEGER
;
params
[
0
].
integer
.
value
=
enable
;
snprintf
(
method
,
5
,
"WE%02X"
,
block
->
notify_id
);
status
=
acpi_evaluate_object
(
handle
,
method
,
&
input
,
NULL
);
if
(
status
!=
AE_OK
&&
status
!=
AE_NOT_FOUND
)
return
status
;
else
return
AE_OK
;
}
/*
/*
* Exported WMI functions
* Exported WMI functions
*/
*/
...
@@ -427,6 +456,7 @@ acpi_status wmi_install_notify_handler(const char *guid,
...
@@ -427,6 +456,7 @@ acpi_status wmi_install_notify_handler(const char *guid,
wmi_notify_handler
handler
,
void
*
data
)
wmi_notify_handler
handler
,
void
*
data
)
{
{
struct
wmi_block
*
block
;
struct
wmi_block
*
block
;
acpi_status
status
;
if
(
!
guid
||
!
handler
)
if
(
!
guid
||
!
handler
)
return
AE_BAD_PARAMETER
;
return
AE_BAD_PARAMETER
;
...
@@ -441,7 +471,9 @@ wmi_notify_handler handler, void *data)
...
@@ -441,7 +471,9 @@ wmi_notify_handler handler, void *data)
block
->
handler
=
handler
;
block
->
handler
=
handler
;
block
->
handler_data
=
data
;
block
->
handler_data
=
data
;
return
AE_OK
;
status
=
wmi_method_enable
(
block
,
1
);
return
status
;
}
}
EXPORT_SYMBOL_GPL
(
wmi_install_notify_handler
);
EXPORT_SYMBOL_GPL
(
wmi_install_notify_handler
);
...
@@ -453,6 +485,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
...
@@ -453,6 +485,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
acpi_status
wmi_remove_notify_handler
(
const
char
*
guid
)
acpi_status
wmi_remove_notify_handler
(
const
char
*
guid
)
{
{
struct
wmi_block
*
block
;
struct
wmi_block
*
block
;
acpi_status
status
;
if
(
!
guid
)
if
(
!
guid
)
return
AE_BAD_PARAMETER
;
return
AE_BAD_PARAMETER
;
...
@@ -464,10 +497,12 @@ acpi_status wmi_remove_notify_handler(const char *guid)
...
@@ -464,10 +497,12 @@ acpi_status wmi_remove_notify_handler(const char *guid)
if
(
!
block
->
handler
)
if
(
!
block
->
handler
)
return
AE_NULL_ENTRY
;
return
AE_NULL_ENTRY
;
status
=
wmi_method_enable
(
block
,
0
);
block
->
handler
=
NULL
;
block
->
handler
=
NULL
;
block
->
handler_data
=
NULL
;
block
->
handler_data
=
NULL
;
return
AE_OK
;
return
status
;
}
}
EXPORT_SYMBOL_GPL
(
wmi_remove_notify_handler
);
EXPORT_SYMBOL_GPL
(
wmi_remove_notify_handler
);
...
...
drivers/misc/Kconfig
View file @
aa58329f
...
@@ -145,6 +145,7 @@ config ACER_WMI
...
@@ -145,6 +145,7 @@ config ACER_WMI
depends on NEW_LEDS
depends on NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on BACKLIGHT_CLASS_DEVICE
depends on SERIO_I8042
depends on SERIO_I8042
depends on RFKILL
select ACPI_WMI
select ACPI_WMI
---help---
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
This is a driver for newer Acer (and Wistron) laptops. It adds
...
...
drivers/misc/acer-wmi.c
View file @
aa58329f
...
@@ -33,6 +33,8 @@
...
@@ -33,6 +33,8 @@
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/acpi.h>
#include <linux/i8042.h>
#include <linux/i8042.h>
#include <linux/rfkill.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/debugfs.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_drivers.h>
...
@@ -123,21 +125,15 @@ enum interface_flags {
...
@@ -123,21 +125,15 @@ enum interface_flags {
static
int
max_brightness
=
0xF
;
static
int
max_brightness
=
0xF
;
static
int
wireless
=
-
1
;
static
int
bluetooth
=
-
1
;
static
int
mailled
=
-
1
;
static
int
mailled
=
-
1
;
static
int
brightness
=
-
1
;
static
int
brightness
=
-
1
;
static
int
threeg
=
-
1
;
static
int
threeg
=
-
1
;
static
int
force_series
;
static
int
force_series
;
module_param
(
mailled
,
int
,
0444
);
module_param
(
mailled
,
int
,
0444
);
module_param
(
wireless
,
int
,
0444
);
module_param
(
bluetooth
,
int
,
0444
);
module_param
(
brightness
,
int
,
0444
);
module_param
(
brightness
,
int
,
0444
);
module_param
(
threeg
,
int
,
0444
);
module_param
(
threeg
,
int
,
0444
);
module_param
(
force_series
,
int
,
0444
);
module_param
(
force_series
,
int
,
0444
);
MODULE_PARM_DESC
(
wireless
,
"Set initial state of Wireless hardware"
);
MODULE_PARM_DESC
(
bluetooth
,
"Set initial state of Bluetooth hardware"
);
MODULE_PARM_DESC
(
mailled
,
"Set initial state of Mail LED"
);
MODULE_PARM_DESC
(
mailled
,
"Set initial state of Mail LED"
);
MODULE_PARM_DESC
(
brightness
,
"Set initial LCD backlight brightness"
);
MODULE_PARM_DESC
(
brightness
,
"Set initial LCD backlight brightness"
);
MODULE_PARM_DESC
(
threeg
,
"Set initial state of 3G hardware"
);
MODULE_PARM_DESC
(
threeg
,
"Set initial state of 3G hardware"
);
...
@@ -145,8 +141,6 @@ MODULE_PARM_DESC(force_series, "Force a different laptop series");
...
@@ -145,8 +141,6 @@ MODULE_PARM_DESC(force_series, "Force a different laptop series");
struct
acer_data
{
struct
acer_data
{
int
mailled
;
int
mailled
;
int
wireless
;
int
bluetooth
;
int
threeg
;
int
threeg
;
int
brightness
;
int
brightness
;
};
};
...
@@ -157,6 +151,9 @@ struct acer_debug {
...
@@ -157,6 +151,9 @@ struct acer_debug {
u32
wmid_devices
;
u32
wmid_devices
;
};
};
static
struct
rfkill
*
wireless_rfkill
;
static
struct
rfkill
*
bluetooth_rfkill
;
/* Each low-level interface must define at least some of the following */
/* Each low-level interface must define at least some of the following */
struct
wmi_interface
{
struct
wmi_interface
{
/* The WMI device type */
/* The WMI device type */
...
@@ -846,8 +843,6 @@ static void __init acer_commandline_init(void)
...
@@ -846,8 +843,6 @@ static void __init acer_commandline_init(void)
* capability isn't available on the given interface
* capability isn't available on the given interface
*/
*/
set_u32
(
mailled
,
ACER_CAP_MAILLED
);
set_u32
(
mailled
,
ACER_CAP_MAILLED
);
set_u32
(
wireless
,
ACER_CAP_WIRELESS
);
set_u32
(
bluetooth
,
ACER_CAP_BLUETOOTH
);
set_u32
(
threeg
,
ACER_CAP_THREEG
);
set_u32
(
threeg
,
ACER_CAP_THREEG
);
set_u32
(
brightness
,
ACER_CAP_BRIGHTNESS
);
set_u32
(
brightness
,
ACER_CAP_BRIGHTNESS
);
}
}
...
@@ -933,40 +928,135 @@ static void acer_backlight_exit(void)
...
@@ -933,40 +928,135 @@ static void acer_backlight_exit(void)
}
}
/*
/*
* R
ead/ write bool sysfs macro
* R
fkill devices
*/
*/
#define show_set_bool(value, cap) \
static
void
acer_rfkill_update
(
struct
work_struct
*
ignored
);
static ssize_t \
static
DECLARE_DELAYED_WORK
(
acer_rfkill_work
,
acer_rfkill_update
);
show_bool_##value(struct device *dev, struct device_attribute *attr, \
static
void
acer_rfkill_update
(
struct
work_struct
*
ignored
)
char *buf) \
{
{ \
u32
state
;
u32 result; \
acpi_status
status
;
acpi_status status = get_u32(&result, cap); \
if (ACPI_SUCCESS(status)) \
status
=
get_u32
(
&
state
,
ACER_CAP_WIRELESS
);
return sprintf(buf, "%u\n", result); \
if
(
ACPI_SUCCESS
(
status
))
return sprintf(buf, "Read error\n"); \
rfkill_force_state
(
wireless_rfkill
,
state
?
} \
RFKILL_STATE_UNBLOCKED
:
RFKILL_STATE_SOFT_BLOCKED
);
\
static ssize_t \
if
(
has_cap
(
ACER_CAP_BLUETOOTH
))
{
set_bool_##value(struct device *dev, struct device_attribute *attr, \
status
=
get_u32
(
&
state
,
ACER_CAP_BLUETOOTH
);
const char *buf, size_t count) \
if
(
ACPI_SUCCESS
(
status
))
{ \
rfkill_force_state
(
bluetooth_rfkill
,
state
?
u32 tmp = simple_strtoul(buf, NULL, 10); \
RFKILL_STATE_UNBLOCKED
:
acpi_status status = set_u32(tmp, cap); \
RFKILL_STATE_SOFT_BLOCKED
);
if (ACPI_FAILURE(status)) \
}
return -EINVAL; \
return count; \
schedule_delayed_work
(
&
acer_rfkill_work
,
round_jiffies_relative
(
HZ
));
} \
}
static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
show_bool_##value, set_bool_##value);
static
int
acer_rfkill_set
(
void
*
data
,
enum
rfkill_state
state
)
{
show_set_bool
(
wireless
,
ACER_CAP_WIRELESS
);
acpi_status
status
;
show_set_bool
(
bluetooth
,
ACER_CAP_BLUETOOTH
);
u32
*
cap
=
data
;
show_set_bool
(
threeg
,
ACER_CAP_THREEG
);
status
=
set_u32
((
u32
)
(
state
==
RFKILL_STATE_UNBLOCKED
),
*
cap
);
if
(
ACPI_FAILURE
(
status
))
return
-
ENODEV
;
return
0
;
}
static
struct
rfkill
*
acer_rfkill_register
(
struct
device
*
dev
,
enum
rfkill_type
type
,
char
*
name
,
u32
cap
)
{
int
err
;
u32
state
;
u32
*
data
;
struct
rfkill
*
rfkill_dev
;
rfkill_dev
=
rfkill_allocate
(
dev
,
type
);
if
(
!
rfkill_dev
)
return
ERR_PTR
(
-
ENOMEM
);
rfkill_dev
->
name
=
name
;
get_u32
(
&
state
,
cap
);
rfkill_dev
->
state
=
state
?
RFKILL_STATE_UNBLOCKED
:
RFKILL_STATE_SOFT_BLOCKED
;
data
=
kzalloc
(
sizeof
(
u32
),
GFP_KERNEL
);
if
(
!
data
)
{
rfkill_free
(
rfkill_dev
);
return
ERR_PTR
(
-
ENOMEM
);
}
*
data
=
cap
;
rfkill_dev
->
data
=
data
;
rfkill_dev
->
toggle_radio
=
acer_rfkill_set
;
rfkill_dev
->
user_claim_unsupported
=
1
;
err
=
rfkill_register
(
rfkill_dev
);
if
(
err
)
{
kfree
(
rfkill_dev
->
data
);
rfkill_free
(
rfkill_dev
);
return
ERR_PTR
(
err
);
}
return
rfkill_dev
;
}
static
int
acer_rfkill_init
(
struct
device
*
dev
)
{
wireless_rfkill
=
acer_rfkill_register
(
dev
,
RFKILL_TYPE_WLAN
,
"acer-wireless"
,
ACER_CAP_WIRELESS
);
if
(
IS_ERR
(
wireless_rfkill
))
return
PTR_ERR
(
wireless_rfkill
);
if
(
has_cap
(
ACER_CAP_BLUETOOTH
))
{
bluetooth_rfkill
=
acer_rfkill_register
(
dev
,
RFKILL_TYPE_BLUETOOTH
,
"acer-bluetooth"
,
ACER_CAP_BLUETOOTH
);
if
(
IS_ERR
(
bluetooth_rfkill
))
{
kfree
(
wireless_rfkill
->
data
);
rfkill_unregister
(
wireless_rfkill
);
return
PTR_ERR
(
bluetooth_rfkill
);
}
}
schedule_delayed_work
(
&
acer_rfkill_work
,
round_jiffies_relative
(
HZ
));
return
0
;
}
static
void
acer_rfkill_exit
(
void
)
{
cancel_delayed_work_sync
(
&
acer_rfkill_work
);
kfree
(
wireless_rfkill
->
data
);
rfkill_unregister
(
wireless_rfkill
);
if
(
has_cap
(
ACER_CAP_BLUETOOTH
))
{
kfree
(
wireless_rfkill
->
data
);
rfkill_unregister
(
bluetooth_rfkill
);
}
return
;
}
/*
/*
*
Read interface sysfs macro
*
sysfs interface
*/
*/
static
ssize_t
show_bool_threeg
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
u32
result
;
\
acpi_status
status
=
get_u32
(
&
result
,
ACER_CAP_THREEG
);
if
(
ACPI_SUCCESS
(
status
))
return
sprintf
(
buf
,
"%u
\n
"
,
result
);
return
sprintf
(
buf
,
"Read error
\n
"
);
}
static
ssize_t
set_bool_threeg
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
u32
tmp
=
simple_strtoul
(
buf
,
NULL
,
10
);
acpi_status
status
=
set_u32
(
tmp
,
ACER_CAP_THREEG
);
if
(
ACPI_FAILURE
(
status
))
return
-
EINVAL
;
return
count
;
}
static
DEVICE_ATTR
(
threeg
,
S_IWUGO
|
S_IRUGO
|
S_IWUSR
,
show_bool_threeg
,
set_bool_threeg
);
static
ssize_t
show_interface
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
static
ssize_t
show_interface
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
char
*
buf
)
{
{
...
@@ -1026,7 +1116,9 @@ static int __devinit acer_platform_probe(struct platform_device *device)
...
@@ -1026,7 +1116,9 @@ static int __devinit acer_platform_probe(struct platform_device *device)
goto
error_brightness
;
goto
error_brightness
;
}
}
return
0
;
err
=
acer_rfkill_init
(
&
device
->
dev
);
return
err
;
error_brightness:
error_brightness:
acer_led_exit
();
acer_led_exit
();
...
@@ -1040,6 +1132,8 @@ static int acer_platform_remove(struct platform_device *device)
...
@@ -1040,6 +1132,8 @@ static int acer_platform_remove(struct platform_device *device)
acer_led_exit
();
acer_led_exit
();
if
(
has_cap
(
ACER_CAP_BRIGHTNESS
))
if
(
has_cap
(
ACER_CAP_BRIGHTNESS
))
acer_backlight_exit
();
acer_backlight_exit
();
acer_rfkill_exit
();
return
0
;
return
0
;
}
}
...
@@ -1052,16 +1146,6 @@ pm_message_t state)
...
@@ -1052,16 +1146,6 @@ pm_message_t state)
if
(
!
data
)
if
(
!
data
)
return
-
ENOMEM
;
return
-
ENOMEM
;
if
(
has_cap
(
ACER_CAP_WIRELESS
))
{
get_u32
(
&
value
,
ACER_CAP_WIRELESS
);
data
->
wireless
=
value
;
}
if
(
has_cap
(
ACER_CAP_BLUETOOTH
))
{
get_u32
(
&
value
,
ACER_CAP_BLUETOOTH
);
data
->
bluetooth
=
value
;
}
if
(
has_cap
(
ACER_CAP_MAILLED
))
{
if
(
has_cap
(
ACER_CAP_MAILLED
))
{
get_u32
(
&
value
,
ACER_CAP_MAILLED
);
get_u32
(
&
value
,
ACER_CAP_MAILLED
);
data
->
mailled
=
value
;
data
->
mailled
=
value
;
...
@@ -1082,15 +1166,6 @@ static int acer_platform_resume(struct platform_device *device)
...
@@ -1082,15 +1166,6 @@ static int acer_platform_resume(struct platform_device *device)
if
(
!
data
)
if
(
!
data
)
return
-
ENOMEM
;
return
-
ENOMEM
;
if
(
has_cap
(
ACER_CAP_WIRELESS
))
set_u32
(
data
->
wireless
,
ACER_CAP_WIRELESS
);
if
(
has_cap
(
ACER_CAP_BLUETOOTH
))
set_u32
(
data
->
bluetooth
,
ACER_CAP_BLUETOOTH
);
if
(
has_cap
(
ACER_CAP_THREEG
))
set_u32
(
data
->
threeg
,
ACER_CAP_THREEG
);
if
(
has_cap
(
ACER_CAP_MAILLED
))
if
(
has_cap
(
ACER_CAP_MAILLED
))
set_u32
(
data
->
mailled
,
ACER_CAP_MAILLED
);
set_u32
(
data
->
mailled
,
ACER_CAP_MAILLED
);
...
@@ -1115,12 +1190,6 @@ static struct platform_device *acer_platform_device;
...
@@ -1115,12 +1190,6 @@ static struct platform_device *acer_platform_device;
static
int
remove_sysfs
(
struct
platform_device
*
device
)
static
int
remove_sysfs
(
struct
platform_device
*
device
)
{
{
if
(
has_cap
(
ACER_CAP_WIRELESS
))
device_remove_file
(
&
device
->
dev
,
&
dev_attr_wireless
);
if
(
has_cap
(
ACER_CAP_BLUETOOTH
))
device_remove_file
(
&
device
->
dev
,
&
dev_attr_bluetooth
);
if
(
has_cap
(
ACER_CAP_THREEG
))
if
(
has_cap
(
ACER_CAP_THREEG
))
device_remove_file
(
&
device
->
dev
,
&
dev_attr_threeg
);
device_remove_file
(
&
device
->
dev
,
&
dev_attr_threeg
);
...
@@ -1133,20 +1202,6 @@ static int create_sysfs(void)
...
@@ -1133,20 +1202,6 @@ static int create_sysfs(void)
{
{
int
retval
=
-
ENOMEM
;
int
retval
=
-
ENOMEM
;
if
(
has_cap
(
ACER_CAP_WIRELESS
))
{
retval
=
device_create_file
(
&
acer_platform_device
->
dev
,
&
dev_attr_wireless
);
if
(
retval
)
goto
error_sysfs
;
}
if
(
has_cap
(
ACER_CAP_BLUETOOTH
))
{
retval
=
device_create_file
(
&
acer_platform_device
->
dev
,
&
dev_attr_bluetooth
);
if
(
retval
)
goto
error_sysfs
;
}
if
(
has_cap
(
ACER_CAP_THREEG
))
{
if
(
has_cap
(
ACER_CAP_THREEG
))
{
retval
=
device_create_file
(
&
acer_platform_device
->
dev
,
retval
=
device_create_file
(
&
acer_platform_device
->
dev
,
&
dev_attr_threeg
);
&
dev_attr_threeg
);
...
...
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