Commit e69ec487 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID fixes from Jiri Kosina:

 - fix for OOB in hiddev, from Dmitry Torokhov

 - _poll API fixes for hidraw, from Marcel Holtmann

 - functional fix for Steam driver, from Rodrigo Rivas Costa

 - a few new device IDs / device-specific quirks and other assorted
   smaller fixes

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  HID: steam: Fix input device disappearing
  HID: intel-ish-hid: ipc: Add Tiger Lake PCI device ID
  drivers/hid/hid-multitouch.c: fix a possible null pointer access.
  HID: wacom: Recognize new MobileStudio Pro PID
  HID: intel-ish-hid: ipc: add CMP device id
  HID: hiddev: fix mess in hiddev_open()
  HID: hid-input: clear unmapped usages
  HID: Add quirk for incorrect input length on Lenovo Y720
  HID: asus: Ignore Asus vendor-page usage-code 0xff events
  HID: ite: Add USB id match for Acer SW5-012 keyboard dock
  HID: Add quirk for Xin-Mo Dual Controller
  HID: Fix slab-out-of-bounds read in hid_field_extract
  HID: multitouch: Add LG MELF0410 I2C touchscreen support
  HID: uhid: Fix returning EPOLLOUT from uhid_char_poll
  HID: hidraw: Fix returning EPOLLOUT from hidraw_poll
parents a5f48c78 20eee6e5
...@@ -261,7 +261,8 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field, ...@@ -261,7 +261,8 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value) struct hid_usage *usage, __s32 value)
{ {
if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 && if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
(usage->hid & HID_USAGE) != 0x00 && !usage->type) { (usage->hid & HID_USAGE) != 0x00 &&
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n", hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
usage->hid & HID_USAGE); usage->hid & HID_USAGE);
} }
......
...@@ -288,6 +288,12 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign ...@@ -288,6 +288,12 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
offset = report->size; offset = report->size;
report->size += parser->global.report_size * parser->global.report_count; report->size += parser->global.report_size * parser->global.report_count;
/* Total size check: Allow for possible report index byte */
if (report->size > (HID_MAX_BUFFER_SIZE - 1) << 3) {
hid_err(parser->device, "report is too long\n");
return -1;
}
if (!parser->local.usage_index) /* Ignore padding fields */ if (!parser->local.usage_index) /* Ignore padding fields */
return 0; return 0;
......
...@@ -631,6 +631,7 @@ ...@@ -631,6 +631,7 @@
#define USB_VENDOR_ID_ITE 0x048d #define USB_VENDOR_ID_ITE 0x048d
#define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386 #define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386
#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 #define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350
#define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a
#define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396 #define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396
#define USB_DEVICE_ID_ITE8595 0x8595 #define USB_DEVICE_ID_ITE8595 0x8595
...@@ -730,6 +731,7 @@ ...@@ -730,6 +731,7 @@
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
#define USB_DEVICE_ID_LG_MELFAS_MT 0x6007 #define USB_DEVICE_ID_LG_MELFAS_MT 0x6007
#define I2C_DEVICE_ID_LG_8001 0x8001 #define I2C_DEVICE_ID_LG_8001 0x8001
#define I2C_DEVICE_ID_LG_7010 0x7010
#define USB_VENDOR_ID_LOGITECH 0x046d #define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
...@@ -1102,6 +1104,7 @@ ...@@ -1102,6 +1104,7 @@
#define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10 #define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10
#define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 #define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3
#define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3
#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012 0x2968
#define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 #define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710
#define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5 0x81a7 #define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5 0x81a7
......
...@@ -1132,9 +1132,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -1132,9 +1132,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
} }
mapped: mapped:
if (device->driver->input_mapped && device->driver->input_mapped(device, if (device->driver->input_mapped &&
hidinput, field, usage, &bit, &max) < 0) device->driver->input_mapped(device, hidinput, field, usage,
goto ignore; &bit, &max) < 0) {
/*
* The driver indicated that no further generic handling
* of the usage is desired.
*/
return;
}
set_bit(usage->type, input->evbit); set_bit(usage->type, input->evbit);
...@@ -1215,9 +1221,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -1215,9 +1221,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
set_bit(MSC_SCAN, input->mscbit); set_bit(MSC_SCAN, input->mscbit);
} }
ignore:
return; return;
ignore:
usage->type = 0;
usage->code = 0;
} }
static void hidinput_handle_scroll(struct hid_usage *usage, static void hidinput_handle_scroll(struct hid_usage *usage,
......
...@@ -40,6 +40,9 @@ static int ite_event(struct hid_device *hdev, struct hid_field *field, ...@@ -40,6 +40,9 @@ static int ite_event(struct hid_device *hdev, struct hid_field *field,
static const struct hid_device_id ite_devices[] = { static const struct hid_device_id ite_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) },
{ HID_USB_DEVICE(USB_VENDOR_ID_258A, USB_DEVICE_ID_258A_6A88) }, { HID_USB_DEVICE(USB_VENDOR_ID_258A, USB_DEVICE_ID_258A_6A88) },
/* ITE8595 USB kbd ctlr, with Synaptics touchpad connected to it. */
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS,
USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012) },
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, ite_devices); MODULE_DEVICE_TABLE(hid, ite_devices);
......
...@@ -1019,7 +1019,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input, ...@@ -1019,7 +1019,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
tool = MT_TOOL_DIAL; tool = MT_TOOL_DIAL;
else if (unlikely(!confidence_state)) { else if (unlikely(!confidence_state)) {
tool = MT_TOOL_PALM; tool = MT_TOOL_PALM;
if (!active && if (!active && mt &&
input_mt_is_active(&mt->slots[slotnum])) { input_mt_is_active(&mt->slots[slotnum])) {
/* /*
* The non-confidence was reported for * The non-confidence was reported for
...@@ -1985,6 +1985,9 @@ static const struct hid_device_id mt_devices[] = { ...@@ -1985,6 +1985,9 @@ static const struct hid_device_id mt_devices[] = {
{ .driver_data = MT_CLS_LG, { .driver_data = MT_CLS_LG,
HID_USB_DEVICE(USB_VENDOR_ID_LG, HID_USB_DEVICE(USB_VENDOR_ID_LG,
USB_DEVICE_ID_LG_MELFAS_MT) }, USB_DEVICE_ID_LG_MELFAS_MT) },
{ .driver_data = MT_CLS_LG,
HID_DEVICE(BUS_I2C, HID_GROUP_GENERIC,
USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_7010) },
/* MosArt panels */ /* MosArt panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
......
...@@ -174,6 +174,7 @@ static const struct hid_device_id hid_quirks[] = { ...@@ -174,6 +174,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT },
{ 0 } { 0 }
}; };
......
...@@ -768,8 +768,12 @@ static int steam_probe(struct hid_device *hdev, ...@@ -768,8 +768,12 @@ static int steam_probe(struct hid_device *hdev,
if (steam->quirks & STEAM_QUIRK_WIRELESS) { if (steam->quirks & STEAM_QUIRK_WIRELESS) {
hid_info(hdev, "Steam wireless receiver connected"); hid_info(hdev, "Steam wireless receiver connected");
/* If using a wireless adaptor ask for connection status */
steam->connected = false;
steam_request_conn_status(steam); steam_request_conn_status(steam);
} else { } else {
/* A wired connection is always present */
steam->connected = true;
ret = steam_register(steam); ret = steam_register(steam);
if (ret) { if (ret) {
hid_err(hdev, hid_err(hdev,
......
...@@ -252,10 +252,10 @@ static __poll_t hidraw_poll(struct file *file, poll_table *wait) ...@@ -252,10 +252,10 @@ static __poll_t hidraw_poll(struct file *file, poll_table *wait)
poll_wait(file, &list->hidraw->wait, wait); poll_wait(file, &list->hidraw->wait, wait);
if (list->head != list->tail) if (list->head != list->tail)
return EPOLLIN | EPOLLRDNORM | EPOLLOUT; return EPOLLIN | EPOLLRDNORM;
if (!list->hidraw->exist) if (!list->hidraw->exist)
return EPOLLERR | EPOLLHUP; return EPOLLERR | EPOLLHUP;
return 0; return EPOLLOUT | EPOLLWRNORM;
} }
static int hidraw_open(struct inode *inode, struct file *file) static int hidraw_open(struct inode *inode, struct file *file)
......
...@@ -49,6 +49,8 @@ ...@@ -49,6 +49,8 @@
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1) #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4) #define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
#define I2C_HID_QUIRK_RESET_ON_RESUME BIT(5) #define I2C_HID_QUIRK_RESET_ON_RESUME BIT(5)
#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6)
/* flags */ /* flags */
#define I2C_HID_STARTED 0 #define I2C_HID_STARTED 0
...@@ -175,6 +177,8 @@ static const struct i2c_hid_quirks { ...@@ -175,6 +177,8 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_BOGUS_IRQ }, I2C_HID_QUIRK_BOGUS_IRQ },
{ USB_VENDOR_ID_ALPS_JP, HID_ANY_ID, { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID,
I2C_HID_QUIRK_RESET_ON_RESUME }, I2C_HID_QUIRK_RESET_ON_RESUME },
{ USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720,
I2C_HID_QUIRK_BAD_INPUT_SIZE },
{ 0, 0 } { 0, 0 }
}; };
...@@ -496,9 +500,15 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) ...@@ -496,9 +500,15 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
} }
if ((ret_size > size) || (ret_size < 2)) { if ((ret_size > size) || (ret_size < 2)) {
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", if (ihid->quirks & I2C_HID_QUIRK_BAD_INPUT_SIZE) {
__func__, size, ret_size); ihid->inbuf[0] = size & 0xff;
return; ihid->inbuf[1] = size >> 8;
ret_size = size;
} else {
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
__func__, size, ret_size);
return;
}
} }
i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf); i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf);
......
...@@ -24,7 +24,9 @@ ...@@ -24,7 +24,9 @@
#define ICL_MOBILE_DEVICE_ID 0x34FC #define ICL_MOBILE_DEVICE_ID 0x34FC
#define SPT_H_DEVICE_ID 0xA135 #define SPT_H_DEVICE_ID 0xA135
#define CML_LP_DEVICE_ID 0x02FC #define CML_LP_DEVICE_ID 0x02FC
#define CMP_H_DEVICE_ID 0x06FC
#define EHL_Ax_DEVICE_ID 0x4BB3 #define EHL_Ax_DEVICE_ID 0x4BB3
#define TGL_LP_DEVICE_ID 0xA0FC
#define REVISION_ID_CHT_A0 0x6 #define REVISION_ID_CHT_A0 0x6
#define REVISION_ID_CHT_Ax_SI 0x0 #define REVISION_ID_CHT_Ax_SI 0x0
......
...@@ -34,7 +34,9 @@ static const struct pci_device_id ish_pci_tbl[] = { ...@@ -34,7 +34,9 @@ static const struct pci_device_id ish_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)},
{0, } {0, }
}; };
MODULE_DEVICE_TABLE(pci, ish_pci_tbl); MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
......
...@@ -772,7 +772,7 @@ static __poll_t uhid_char_poll(struct file *file, poll_table *wait) ...@@ -772,7 +772,7 @@ static __poll_t uhid_char_poll(struct file *file, poll_table *wait)
if (uhid->head != uhid->tail) if (uhid->head != uhid->tail)
return EPOLLIN | EPOLLRDNORM; return EPOLLIN | EPOLLRDNORM;
return 0; return EPOLLOUT | EPOLLWRNORM;
} }
static const struct file_operations uhid_fops = { static const struct file_operations uhid_fops = {
......
...@@ -241,12 +241,51 @@ static int hiddev_release(struct inode * inode, struct file * file) ...@@ -241,12 +241,51 @@ static int hiddev_release(struct inode * inode, struct file * file)
return 0; return 0;
} }
static int __hiddev_open(struct hiddev *hiddev, struct file *file)
{
struct hiddev_list *list;
int error;
lockdep_assert_held(&hiddev->existancelock);
list = vzalloc(sizeof(*list));
if (!list)
return -ENOMEM;
mutex_init(&list->thread_lock);
list->hiddev = hiddev;
if (!hiddev->open++) {
error = hid_hw_power(hiddev->hid, PM_HINT_FULLON);
if (error < 0)
goto err_drop_count;
error = hid_hw_open(hiddev->hid);
if (error < 0)
goto err_normal_power;
}
spin_lock_irq(&hiddev->list_lock);
list_add_tail(&list->node, &hiddev->list);
spin_unlock_irq(&hiddev->list_lock);
file->private_data = list;
return 0;
err_normal_power:
hid_hw_power(hiddev->hid, PM_HINT_NORMAL);
err_drop_count:
hiddev->open--;
vfree(list);
return error;
}
/* /*
* open file op * open file op
*/ */
static int hiddev_open(struct inode *inode, struct file *file) static int hiddev_open(struct inode *inode, struct file *file)
{ {
struct hiddev_list *list;
struct usb_interface *intf; struct usb_interface *intf;
struct hid_device *hid; struct hid_device *hid;
struct hiddev *hiddev; struct hiddev *hiddev;
...@@ -255,66 +294,14 @@ static int hiddev_open(struct inode *inode, struct file *file) ...@@ -255,66 +294,14 @@ static int hiddev_open(struct inode *inode, struct file *file)
intf = usbhid_find_interface(iminor(inode)); intf = usbhid_find_interface(iminor(inode));
if (!intf) if (!intf)
return -ENODEV; return -ENODEV;
hid = usb_get_intfdata(intf); hid = usb_get_intfdata(intf);
hiddev = hid->hiddev; hiddev = hid->hiddev;
if (!(list = vzalloc(sizeof(struct hiddev_list))))
return -ENOMEM;
mutex_init(&list->thread_lock);
list->hiddev = hiddev;
file->private_data = list;
/*
* no need for locking because the USB major number
* is shared which usbcore guards against disconnect
*/
if (list->hiddev->exist) {
if (!list->hiddev->open++) {
res = hid_hw_open(hiddev->hid);
if (res < 0)
goto bail;
}
} else {
res = -ENODEV;
goto bail;
}
spin_lock_irq(&list->hiddev->list_lock);
list_add_tail(&list->node, &hiddev->list);
spin_unlock_irq(&list->hiddev->list_lock);
mutex_lock(&hiddev->existancelock); mutex_lock(&hiddev->existancelock);
/* res = hiddev->exist ? __hiddev_open(hiddev, file) : -ENODEV;
* recheck exist with existance lock held to
* avoid opening a disconnected device
*/
if (!list->hiddev->exist) {
res = -ENODEV;
goto bail_unlock;
}
if (!list->hiddev->open++)
if (list->hiddev->exist) {
struct hid_device *hid = hiddev->hid;
res = hid_hw_power(hid, PM_HINT_FULLON);
if (res < 0)
goto bail_unlock;
res = hid_hw_open(hid);
if (res < 0)
goto bail_normal_power;
}
mutex_unlock(&hiddev->existancelock);
return 0;
bail_normal_power:
hid_hw_power(hid, PM_HINT_NORMAL);
bail_unlock:
mutex_unlock(&hiddev->existancelock); mutex_unlock(&hiddev->existancelock);
spin_lock_irq(&list->hiddev->list_lock);
list_del(&list->node);
spin_unlock_irq(&list->hiddev->list_lock);
bail:
file->private_data = NULL;
vfree(list);
return res; return res;
} }
......
...@@ -2096,14 +2096,16 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field ...@@ -2096,14 +2096,16 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
(hdev->product == 0x34d || hdev->product == 0x34e || /* MobileStudio Pro */ (hdev->product == 0x34d || hdev->product == 0x34e || /* MobileStudio Pro */
hdev->product == 0x357 || hdev->product == 0x358 || /* Intuos Pro 2 */ hdev->product == 0x357 || hdev->product == 0x358 || /* Intuos Pro 2 */
hdev->product == 0x392 || /* Intuos Pro 2 */ hdev->product == 0x392 || /* Intuos Pro 2 */
hdev->product == 0x398 || hdev->product == 0x399)) { /* MobileStudio Pro */ hdev->product == 0x398 || hdev->product == 0x399 || /* MobileStudio Pro */
hdev->product == 0x3AA)) { /* MobileStudio Pro */
value = (field->logical_maximum - value); value = (field->logical_maximum - value);
if (hdev->product == 0x357 || hdev->product == 0x358 || if (hdev->product == 0x357 || hdev->product == 0x358 ||
hdev->product == 0x392) hdev->product == 0x392)
value = wacom_offset_rotation(input, usage, value, 3, 16); value = wacom_offset_rotation(input, usage, value, 3, 16);
else if (hdev->product == 0x34d || hdev->product == 0x34e || else if (hdev->product == 0x34d || hdev->product == 0x34e ||
hdev->product == 0x398 || hdev->product == 0x399) hdev->product == 0x398 || hdev->product == 0x399 ||
hdev->product == 0x3AA)
value = wacom_offset_rotation(input, usage, value, 1, 2); value = wacom_offset_rotation(input, usage, value, 1, 2);
} }
else { else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment