1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * u_ether_configfs.h 4 * 5 * Utility definitions for configfs support in USB Ethernet functions 6 * 7 * Copyright (c) 2013 Samsung Electronics Co., Ltd. 8 * http://www.samsung.com 9 * 10 * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> 11 */ 12 13 #ifndef __U_ETHER_CONFIGFS_H 14 #define __U_ETHER_CONFIGFS_H 15 16 #include <linux/cleanup.h> 17 #include <linux/hex.h> 18 #include <linux/if_ether.h> 19 #include <linux/mutex.h> 20 #include <linux/netdevice.h> 21 #include <linux/rtnetlink.h> 22 23 #define USB_ETHERNET_CONFIGFS_ITEM(_f_) \ 24 static void _f_##_attr_release(struct config_item *item) \ 25 { \ 26 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 27 \ 28 usb_put_function_instance(&opts->func_inst); \ 29 } \ 30 \ 31 static const struct configfs_item_operations _f_##_item_ops = { \ 32 .release = _f_##_attr_release, \ 33 } 34 35 #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_) \ 36 static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \ 37 char *page) \ 38 { \ 39 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 40 int result; \ 41 \ 42 mutex_lock(&opts->lock); \ 43 result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \ 44 mutex_unlock(&opts->lock); \ 45 \ 46 return result; \ 47 } \ 48 \ 49 static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \ 50 const char *page, size_t len)\ 51 { \ 52 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 53 int ret; \ 54 \ 55 mutex_lock(&opts->lock); \ 56 if (opts->refcnt) { \ 57 mutex_unlock(&opts->lock); \ 58 return -EBUSY; \ 59 } \ 60 \ 61 ret = gether_set_dev_addr(opts->net, page); \ 62 mutex_unlock(&opts->lock); \ 63 if (!ret) \ 64 ret = len; \ 65 return ret; \ 66 } \ 67 \ 68 CONFIGFS_ATTR(_f_##_opts_, dev_addr) 69 70 #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_) \ 71 static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \ 72 char *page) \ 73 { \ 74 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 75 int result; \ 76 \ 77 mutex_lock(&opts->lock); \ 78 result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \ 79 mutex_unlock(&opts->lock); \ 80 \ 81 return result; \ 82 } \ 83 \ 84 static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \ 85 const char *page, size_t len)\ 86 { \ 87 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 88 int ret; \ 89 \ 90 mutex_lock(&opts->lock); \ 91 if (opts->refcnt) { \ 92 mutex_unlock(&opts->lock); \ 93 return -EBUSY; \ 94 } \ 95 \ 96 ret = gether_set_host_addr(opts->net, page); \ 97 mutex_unlock(&opts->lock); \ 98 if (!ret) \ 99 ret = len; \ 100 return ret; \ 101 } \ 102 \ 103 CONFIGFS_ATTR(_f_##_opts_, host_addr) 104 105 #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_) \ 106 static ssize_t _f_##_opts_qmult_show(struct config_item *item, \ 107 char *page) \ 108 { \ 109 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 110 unsigned qmult; \ 111 \ 112 mutex_lock(&opts->lock); \ 113 qmult = gether_get_qmult(opts->net); \ 114 mutex_unlock(&opts->lock); \ 115 return sprintf(page, "%d\n", qmult); \ 116 } \ 117 \ 118 static ssize_t _f_##_opts_qmult_store(struct config_item *item, \ 119 const char *page, size_t len)\ 120 { \ 121 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 122 u8 val; \ 123 int ret; \ 124 \ 125 mutex_lock(&opts->lock); \ 126 if (opts->refcnt) { \ 127 ret = -EBUSY; \ 128 goto out; \ 129 } \ 130 \ 131 ret = kstrtou8(page, 0, &val); \ 132 if (ret) \ 133 goto out; \ 134 \ 135 gether_set_qmult(opts->net, val); \ 136 ret = len; \ 137 out: \ 138 mutex_unlock(&opts->lock); \ 139 return ret; \ 140 } \ 141 \ 142 CONFIGFS_ATTR(_f_##_opts_, qmult) 143 144 #define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_) \ 145 static ssize_t _f_##_opts_ifname_show(struct config_item *item, \ 146 char *page) \ 147 { \ 148 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 149 int ret; \ 150 \ 151 mutex_lock(&opts->lock); \ 152 ret = gether_get_ifname(opts->net, page, PAGE_SIZE); \ 153 mutex_unlock(&opts->lock); \ 154 \ 155 return ret; \ 156 } \ 157 \ 158 static ssize_t _f_##_opts_ifname_store(struct config_item *item, \ 159 const char *page, size_t len)\ 160 { \ 161 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 162 int ret = -EBUSY; \ 163 \ 164 mutex_lock(&opts->lock); \ 165 if (!opts->refcnt) \ 166 ret = gether_set_ifname(opts->net, page, len); \ 167 mutex_unlock(&opts->lock); \ 168 return ret ?: len; \ 169 } \ 170 \ 171 CONFIGFS_ATTR(_f_##_opts_, ifname) 172 173 #define USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(_f_, _n_) \ 174 static ssize_t _f_##_opts_##_n_##_show(struct config_item *item,\ 175 char *page) \ 176 { \ 177 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 178 int ret; \ 179 \ 180 mutex_lock(&opts->lock); \ 181 ret = sprintf(page, "%02x\n", opts->_n_); \ 182 mutex_unlock(&opts->lock); \ 183 \ 184 return ret; \ 185 } \ 186 \ 187 static ssize_t _f_##_opts_##_n_##_store(struct config_item *item,\ 188 const char *page, \ 189 size_t len) \ 190 { \ 191 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 192 int ret = -EINVAL; \ 193 u8 val; \ 194 \ 195 mutex_lock(&opts->lock); \ 196 if (sscanf(page, "%02hhx", &val) > 0) { \ 197 opts->_n_ = val; \ 198 ret = len; \ 199 } \ 200 mutex_unlock(&opts->lock); \ 201 \ 202 return ret; \ 203 } \ 204 \ 205 CONFIGFS_ATTR(_f_##_opts_, _n_) 206 207 #define USB_ETHER_OPTS_ITEM(_f_) \ 208 static void _f_##_attr_release(struct config_item *item) \ 209 { \ 210 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 211 \ 212 usb_put_function_instance(&opts->func_inst); \ 213 } \ 214 \ 215 static struct configfs_item_operations _f_##_item_ops = { \ 216 .release = _f_##_attr_release, \ 217 } 218 219 #define USB_ETHER_OPTS_ATTR_DEV_ADDR(_f_) \ 220 static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \ 221 char *page) \ 222 { \ 223 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 224 \ 225 guard(mutex)(&opts->lock); \ 226 return sysfs_emit(page, "%pM\n", opts->net_opts.dev_mac); \ 227 } \ 228 \ 229 static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \ 230 const char *page, size_t len) \ 231 { \ 232 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 233 u8 new_addr[ETH_ALEN]; \ 234 const char *p = page; \ 235 \ 236 guard(mutex)(&opts->lock); \ 237 if (opts->refcnt) \ 238 return -EBUSY; \ 239 \ 240 for (int i = 0; i < ETH_ALEN; i++) { \ 241 unsigned char num; \ 242 if ((*p == '.') || (*p == ':')) \ 243 p++; \ 244 num = hex_to_bin(*p++) << 4; \ 245 num |= hex_to_bin(*p++); \ 246 new_addr[i] = num; \ 247 } \ 248 if (!is_valid_ether_addr(new_addr)) \ 249 return -EINVAL; \ 250 memcpy(opts->net_opts.dev_mac, new_addr, ETH_ALEN); \ 251 opts->net_opts.addr_assign_type = NET_ADDR_SET; \ 252 return len; \ 253 } \ 254 \ 255 CONFIGFS_ATTR(_f_##_opts_, dev_addr) 256 257 #define USB_ETHER_OPTS_ATTR_HOST_ADDR(_f_) \ 258 static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \ 259 char *page) \ 260 { \ 261 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 262 \ 263 guard(mutex)(&opts->lock); \ 264 return sysfs_emit(page, "%pM\n", opts->net_opts.host_mac); \ 265 } \ 266 \ 267 static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \ 268 const char *page, size_t len) \ 269 { \ 270 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 271 u8 new_addr[ETH_ALEN]; \ 272 const char *p = page; \ 273 \ 274 guard(mutex)(&opts->lock); \ 275 if (opts->refcnt) \ 276 return -EBUSY; \ 277 \ 278 for (int i = 0; i < ETH_ALEN; i++) { \ 279 unsigned char num; \ 280 if ((*p == '.') || (*p == ':')) \ 281 p++; \ 282 num = hex_to_bin(*p++) << 4; \ 283 num |= hex_to_bin(*p++); \ 284 new_addr[i] = num; \ 285 } \ 286 if (!is_valid_ether_addr(new_addr)) \ 287 return -EINVAL; \ 288 memcpy(opts->net_opts.host_mac, new_addr, ETH_ALEN); \ 289 return len; \ 290 } \ 291 \ 292 CONFIGFS_ATTR(_f_##_opts_, host_addr) 293 294 #define USB_ETHER_OPTS_ATTR_QMULT(_f_) \ 295 static ssize_t _f_##_opts_qmult_show(struct config_item *item, \ 296 char *page) \ 297 { \ 298 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 299 \ 300 guard(mutex)(&opts->lock); \ 301 return sysfs_emit(page, "%u\n", opts->net_opts.qmult); \ 302 } \ 303 \ 304 static ssize_t _f_##_opts_qmult_store(struct config_item *item, \ 305 const char *page, size_t len) \ 306 { \ 307 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 308 u32 val; \ 309 int ret; \ 310 \ 311 guard(mutex)(&opts->lock); \ 312 if (opts->refcnt) \ 313 return -EBUSY; \ 314 \ 315 ret = kstrtou32(page, 0, &val); \ 316 if (ret) \ 317 return ret; \ 318 \ 319 opts->net_opts.qmult = val; \ 320 return len; \ 321 } \ 322 \ 323 CONFIGFS_ATTR(_f_##_opts_, qmult) 324 325 #define USB_ETHER_OPTS_ATTR_IFNAME(_f_) \ 326 static ssize_t _f_##_opts_ifname_show(struct config_item *item, \ 327 char *page) \ 328 { \ 329 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 330 const char *name; \ 331 \ 332 guard(mutex)(&opts->lock); \ 333 rtnl_lock(); \ 334 if (opts->net_opts.ifname_set) \ 335 name = opts->net_opts.name; \ 336 else if (opts->net) \ 337 name = netdev_name(opts->net); \ 338 else \ 339 name = "(inactive net_device)"; \ 340 rtnl_unlock(); \ 341 return sysfs_emit(page, "%s\n", name); \ 342 } \ 343 \ 344 static ssize_t _f_##_opts_ifname_store(struct config_item *item, \ 345 const char *page, size_t len) \ 346 { \ 347 struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \ 348 char tmp[IFNAMSIZ]; \ 349 const char *p; \ 350 size_t c_len = len; \ 351 \ 352 if (c_len > 0 && page[c_len - 1] == '\n') \ 353 c_len--; \ 354 \ 355 if (c_len >= sizeof(tmp)) \ 356 return -E2BIG; \ 357 \ 358 strscpy(tmp, page, c_len + 1); \ 359 if (!dev_valid_name(tmp)) \ 360 return -EINVAL; \ 361 \ 362 /* Require exactly one %d */ \ 363 p = strchr(tmp, '%'); \ 364 if (!p || p[1] != 'd' || strchr(p + 2, '%')) \ 365 return -EINVAL; \ 366 \ 367 guard(mutex)(&opts->lock); \ 368 if (opts->refcnt) \ 369 return -EBUSY; \ 370 strscpy(opts->net_opts.name, tmp, sizeof(opts->net_opts.name)); \ 371 opts->net_opts.ifname_set = true; \ 372 return len; \ 373 } \ 374 \ 375 CONFIGFS_ATTR(_f_##_opts_, ifname) 376 377 #endif /* __U_ETHER_CONFIGFS_H */ 378