xref: /linux/drivers/usb/gadget/function/u_ether_configfs.h (revision 23b0f90ba871f096474e1c27c3d14f455189d2d9)
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