| /* SPDX-License-Identifier: GPL-2.0 */ | |
| /* | |
| * u_ether_configfs.h | |
| * | |
| * Utility definitions for configfs support in USB Ethernet functions | |
| * | |
| * Copyright (c) 2013 Samsung Electronics Co., Ltd. | |
| * http://www.samsung.com | |
| * | |
| * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> | |
| */ | |
| #ifndef __U_ETHER_CONFIGFS_H | |
| #define __U_ETHER_CONFIGFS_H | |
| #define USB_ETHERNET_CONFIGFS_ITEM(_f_) \ | |
| static void _f_##_attr_release(struct config_item *item) \ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| \ | |
| usb_put_function_instance(&opts->func_inst); \ | |
| } \ | |
| \ | |
| static const struct configfs_item_operations _f_##_item_ops = { \ | |
| .release = _f_##_attr_release, \ | |
| } | |
| #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_) \ | |
| static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \ | |
| char *page) \ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int result; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \ | |
| mutex_unlock(&opts->lock); \ | |
| \ | |
| return result; \ | |
| } \ | |
| \ | |
| static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \ | |
| const char *page, size_t len)\ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int ret; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| if (opts->refcnt) { \ | |
| mutex_unlock(&opts->lock); \ | |
| return -EBUSY; \ | |
| } \ | |
| \ | |
| ret = gether_set_dev_addr(opts->net, page); \ | |
| mutex_unlock(&opts->lock); \ | |
| if (!ret) \ | |
| ret = len; \ | |
| return ret; \ | |
| } \ | |
| \ | |
| CONFIGFS_ATTR(_f_##_opts_, dev_addr) | |
| #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_) \ | |
| static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \ | |
| char *page) \ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int result; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \ | |
| mutex_unlock(&opts->lock); \ | |
| \ | |
| return result; \ | |
| } \ | |
| \ | |
| static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \ | |
| const char *page, size_t len)\ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int ret; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| if (opts->refcnt) { \ | |
| mutex_unlock(&opts->lock); \ | |
| return -EBUSY; \ | |
| } \ | |
| \ | |
| ret = gether_set_host_addr(opts->net, page); \ | |
| mutex_unlock(&opts->lock); \ | |
| if (!ret) \ | |
| ret = len; \ | |
| return ret; \ | |
| } \ | |
| \ | |
| CONFIGFS_ATTR(_f_##_opts_, host_addr) | |
| #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_) \ | |
| static ssize_t _f_##_opts_qmult_show(struct config_item *item, \ | |
| char *page) \ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| unsigned qmult; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| qmult = gether_get_qmult(opts->net); \ | |
| mutex_unlock(&opts->lock); \ | |
| return sprintf(page, "%d\n", qmult); \ | |
| } \ | |
| \ | |
| static ssize_t _f_##_opts_qmult_store(struct config_item *item, \ | |
| const char *page, size_t len)\ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| u8 val; \ | |
| int ret; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| if (opts->refcnt) { \ | |
| ret = -EBUSY; \ | |
| goto out; \ | |
| } \ | |
| \ | |
| ret = kstrtou8(page, 0, &val); \ | |
| if (ret) \ | |
| goto out; \ | |
| \ | |
| gether_set_qmult(opts->net, val); \ | |
| ret = len; \ | |
| out: \ | |
| mutex_unlock(&opts->lock); \ | |
| return ret; \ | |
| } \ | |
| \ | |
| CONFIGFS_ATTR(_f_##_opts_, qmult) | |
| #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_) \ | |
| static ssize_t _f_##_opts_ifname_show(struct config_item *item, \ | |
| char *page) \ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int ret; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| ret = gether_get_ifname(opts->net, page, PAGE_SIZE); \ | |
| mutex_unlock(&opts->lock); \ | |
| \ | |
| return ret; \ | |
| } \ | |
| \ | |
| static ssize_t _f_##_opts_ifname_store(struct config_item *item, \ | |
| const char *page, size_t len)\ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int ret = -EBUSY; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| if (!opts->refcnt) \ | |
| ret = gether_set_ifname(opts->net, page, len); \ | |
| mutex_unlock(&opts->lock); \ | |
| return ret ?: len; \ | |
| } \ | |
| \ | |
| CONFIGFS_ATTR(_f_##_opts_, ifname) | |
| #define USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(_f_, _n_) \ | |
| static ssize_t _f_##_opts_##_n_##_show(struct config_item *item,\ | |
| char *page) \ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int ret; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| ret = sprintf(page, "%02x\n", opts->_n_); \ | |
| mutex_unlock(&opts->lock); \ | |
| \ | |
| return ret; \ | |
| } \ | |
| \ | |
| static ssize_t _f_##_opts_##_n_##_store(struct config_item *item,\ | |
| const char *page, \ | |
| size_t len) \ | |
| { \ | |
| struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ | |
| int ret = -EINVAL; \ | |
| u8 val; \ | |
| \ | |
| mutex_lock(&opts->lock); \ | |
| if (sscanf(page, "%02hhx", &val) > 0) { \ | |
| opts->_n_ = val; \ | |
| ret = len; \ | |
| } \ | |
| mutex_unlock(&opts->lock); \ | |
| \ | |
| return ret; \ | |
| } \ | |
| \ | |
| CONFIGFS_ATTR(_f_##_opts_, _n_) | |
| #endif /* __U_ETHER_CONFIGFS_H */ |