1/*******************************************************************************
2
3
4  Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
5
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the Free
8  Software Foundation; either version 2 of the License, or (at your option)
9  any later version.
10
11  This program is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  more details.
15
16  You should have received a copy of the GNU General Public License along with
17  this program; if not, write to the Free Software Foundation, Inc., 59
18  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20  The full GNU General Public License is included in this distribution in the
21  file called LICENSE.
22
23  Contact Information:
24  Linux NICS <linux.nics@intel.com>
25  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
26*******************************************************************************/
27
28/**********************************************************************
29*                                                                       *
30* INTEL CORPORATION                                                     *
31*                                                                       *
32* This software is supplied under the terms of the license included     *
33* above.  All use of this driver must be in accordance with the terms   *
34* of that license.                                                      *
35*                                                                       *
36* Module Name:  e100_proc.c                                             *
37*                                                                       *
38* Abstract:     Functions to handle the proc file system.               *
39*               Create the proc directories and files and run read and  *
40*               write requests from the user                            *
41*                                                                       *
42* Environment:  This file is intended to be specific to the Linux       *
43*               operating system.                                       *
44*                                                                       *
45**********************************************************************/
46
47#include <linux/config.h>
48
49#ifdef CONFIG_PROC_FS
50#include "e100.h"
51/* MDI sleep time is at least 50 ms, in jiffies */
52#define MDI_SLEEP_TIME ((HZ / 20) + 1)
53/***************************************************************************/
54/*       /proc File System Interaface Support Functions                    */
55/***************************************************************************/
56
57static struct proc_dir_entry *adapters_proc_dir = 0;
58
59/* externs from e100_main.c */
60extern char e100_short_driver_name[];
61extern char e100_driver_version[];
62extern struct net_device_stats *e100_get_stats(struct net_device *dev);
63extern char *e100_get_brand_msg(struct e100_private *bdp);
64extern int e100_mdi_write(struct e100_private *, u32, u32, u16);
65
66static void e100_proc_cleanup(void);
67static unsigned char e100_init_proc_dir(void);
68
69#define ADAPTERS_PROC_DIR "PRO_LAN_Adapters"
70#define WRITE_BUF_MAX_LEN 20
71#define READ_BUF_MAX_LEN  256
72#define E100_PE_LEN       25
73
74#define bdp_drv_off(off) (unsigned long)(offsetof(struct e100_private, drv_stats.off))
75#define bdp_prm_off(off) (unsigned long)(offsetof(struct e100_private, params.off))
76
77typedef struct _e100_proc_entry {
78	char *name;
79	read_proc_t *read_proc;
80	write_proc_t *write_proc;
81	unsigned long offset;	/* offset into bdp. ~0 means no value, pass NULL. */
82} e100_proc_entry;
83
84static int
85generic_read(char *page, char **start, off_t off, int count, int *eof, int len)
86{
87	if (len <= off + count)
88		*eof = 1;
89
90	*start = page + off;
91	len -= off;
92	if (len > count)
93		len = count;
94
95	if (len < 0)
96		len = 0;
97
98	return len;
99}
100
101static int
102read_ulong(char *page, char **start, off_t off,
103	   int count, int *eof, unsigned long l)
104{
105	int len;
106
107	len = sprintf(page, "%lu\n", l);
108
109	return generic_read(page, start, off, count, eof, len);
110}
111
112static int
113read_gen_ulong(char *page, char **start, off_t off,
114	       int count, int *eof, void *data)
115{
116	unsigned long val = 0;
117
118	if (data)
119		val = *((unsigned long *) data);
120
121	return read_ulong(page, start, off, count, eof, val);
122}
123
124static int
125read_hwaddr(char *page, char **start, off_t off,
126	    int count, int *eof, unsigned char *hwaddr)
127{
128	int len;
129
130	len = sprintf(page, "%02X:%02X:%02X:%02X:%02X:%02X\n",
131		      hwaddr[0], hwaddr[1], hwaddr[2],
132		      hwaddr[3], hwaddr[4], hwaddr[5]);
133
134	return generic_read(page, start, off, count, eof, len);
135}
136
137static int
138read_descr(char *page, char **start, off_t off, int count, int *eof, void *data)
139{
140	struct e100_private *bdp = data;
141	int len;
142
143	len = sprintf(page, "%s\n", bdp->id_string);
144
145	return generic_read(page, start, off, count, eof, len);
146}
147
148static int
149read_permanent_hwaddr(char *page, char **start, off_t off,
150		      int count, int *eof, void *data)
151{
152	struct e100_private *bdp = data;
153	unsigned char *hwaddr = bdp->perm_node_address;
154
155	return read_hwaddr(page, start, off, count, eof, hwaddr);
156}
157
158static int
159read_part_number(char *page, char **start, off_t off,
160		 int count, int *eof, void *data)
161{
162	struct e100_private *bdp = data;
163	int len;
164
165	len = sprintf(page, "%06lx-%03x\n",
166		      (unsigned long) (bdp->pwa_no >> 8),
167		      (unsigned int) (bdp->pwa_no & 0xFF));
168
169	return generic_read(page, start, off, count, eof, len);
170}
171
172static void
173set_led(struct e100_private *bdp, u16 led_mdi_op)
174{
175	e100_mdi_write(bdp, PHY_82555_LED_SWITCH_CONTROL,
176		       bdp->phy_addr, led_mdi_op);
177
178	set_current_state(TASK_UNINTERRUPTIBLE);
179	schedule_timeout(MDI_SLEEP_TIME);
180
181	/* turn led ownership to the chip */
182	e100_mdi_write(bdp, PHY_82555_LED_SWITCH_CONTROL,
183		       bdp->phy_addr, PHY_82555_LED_NORMAL_CONTROL);
184}
185
186static int
187write_blink_led_timer(struct file *file, const char *buffer,
188		      unsigned long count, void *data)
189{
190	struct e100_private *bdp = data;
191	char s_blink_op[WRITE_BUF_MAX_LEN + 1];
192	char *res;
193	unsigned long i_blink_op;
194
195	if (!buffer)
196		return -EINVAL;
197
198	if (count > WRITE_BUF_MAX_LEN) {
199		count = WRITE_BUF_MAX_LEN;
200	}
201	if (copy_from_user(s_blink_op, buffer, count))
202		return -EFAULT;
203	s_blink_op[count] = '\0';
204	i_blink_op = simple_strtoul(s_blink_op, &res, 0);
205	if (res == s_blink_op) {
206		return -EINVAL;
207	}
208
209	switch (i_blink_op) {
210
211	case LED_OFF:
212		set_led(bdp, PHY_82555_LED_OFF);
213		break;
214	case LED_ON:
215		if (bdp->rev_id >= D101MA_REV_ID)
216			set_led(bdp, PHY_82555_LED_ON_559);
217		else
218			set_led(bdp, PHY_82555_LED_ON_PRE_559);
219
220		break;
221	default:
222		return -EINVAL;
223	}
224
225	return count;
226}
227
228static e100_proc_entry e100_proc_list[] = {
229	{"Description",           read_descr,            0, 0},
230	{"Permanent_HWaddr",      read_permanent_hwaddr, 0, 0},
231	{"Part_Number",           read_part_number,      0, 0},
232	{"\n",},
233	{"Rx_TCP_Checksum_Good",  read_gen_ulong, 0, ~0},
234	{"Rx_TCP_Checksum_Bad",   read_gen_ulong, 0, ~0},
235	{"Tx_TCP_Checksum_Good",  read_gen_ulong, 0, ~0},
236	{"Tx_TCP_Checksum_Bad",   read_gen_ulong, 0, ~0},
237	{"\n",},
238	{"Tx_Abort_Late_Coll",    read_gen_ulong, 0, bdp_drv_off(tx_late_col)},
239	{"Tx_Deferred_Ok",        read_gen_ulong, 0, bdp_drv_off(tx_ok_defrd)},
240	{"Tx_Single_Coll_Ok",     read_gen_ulong, 0, bdp_drv_off(tx_one_retry)},
241	{"Tx_Multi_Coll_Ok",      read_gen_ulong, 0, bdp_drv_off(tx_mt_one_retry)},
242	{"Rx_Long_Length_Errors", read_gen_ulong, 0, ~0},
243	{"\n",},
244	{"Tx_Flow_Control_Pause", read_gen_ulong, 0, bdp_drv_off(xmt_fc_pkts)},
245	{"Rx_Flow_Control_Pause", read_gen_ulong, 0, bdp_drv_off(rcv_fc_pkts)},
246	{"Rx_Flow_Control_Unsup", read_gen_ulong, 0, bdp_drv_off(rcv_fc_unsupported)},
247	{"\n",},
248	{"Tx_TCO_Packets",        read_gen_ulong, 0, bdp_drv_off(xmt_tco_pkts)},
249	{"Rx_TCO_Packets",        read_gen_ulong, 0, bdp_drv_off(rcv_tco_pkts)},
250	{"\n",},
251	{"Rx_Interrupt_Packets",  read_gen_ulong, 0, bdp_drv_off(rx_intr_pkts)},
252	{"Identify_Adapter", 0, write_blink_led_timer, 0},
253	{"", 0, 0, 0}
254};
255
256static int
257read_info(char *page, char **start, off_t off, int count, int *eof, void *data)
258{
259	struct e100_private *bdp = data;
260	e100_proc_entry *pe;
261	int tmp;
262	void *val;
263	int len = 0;
264
265	for (pe = e100_proc_list; pe->name[0]; pe++) {
266		if (pe->name[0] == '\n') {
267			len += sprintf(page + len, "\n");
268			continue;
269		}
270
271		if (pe->read_proc) {
272			if ((len + READ_BUF_MAX_LEN + E100_PE_LEN + 1) >=
273			    PAGE_SIZE)
274				break;
275
276			if (pe->offset != ~0)
277				val = ((char *) bdp) + pe->offset;
278			else
279				val = NULL;
280
281			len += sprintf(page + len, "%-"
282				       __MODULE_STRING(E100_PE_LEN)
283				       "s ", pe->name);
284			len += pe->read_proc(page + len, start, 0,
285					     READ_BUF_MAX_LEN + 1, &tmp, val);
286		}
287	}
288
289	return generic_read(page, start, off, count, eof, len);
290}
291
292static struct proc_dir_entry *
293create_proc_rw(char *name, void *data, struct proc_dir_entry *parent,
294	       read_proc_t * read_proc, write_proc_t * write_proc)
295{
296	struct proc_dir_entry *pdep;
297	mode_t mode = S_IFREG;
298
299	if (write_proc) {
300		mode |= S_IWUSR;
301		if (read_proc) {
302			mode |= S_IRUSR;
303		}
304
305	} else if (read_proc) {
306		mode |= S_IRUGO;
307	}
308
309	if (!(pdep = create_proc_entry(name, mode, parent)))
310		return NULL;
311
312	pdep->read_proc = read_proc;
313	pdep->write_proc = write_proc;
314	pdep->data = data;
315	return pdep;
316}
317
318void
319e100_remove_proc_subdir(struct e100_private *bdp, char *name)
320{
321	e100_proc_entry *pe;
322	char info[256];
323	int len;
324
325	/* If our root /proc dir was not created, there is nothing to remove */
326	if (adapters_proc_dir == NULL) {
327		return;
328	}
329
330	len = strlen(bdp->ifname);
331	strncpy(info, bdp->ifname, sizeof (info));
332	strncat(info + len, ".info", sizeof (info) - len);
333
334	if (bdp->proc_parent) {
335		for (pe = e100_proc_list; pe->name[0]; pe++) {
336			if (pe->name[0] == '\n')
337				continue;
338
339			remove_proc_entry(pe->name, bdp->proc_parent);
340		}
341
342		remove_proc_entry(bdp->ifname, adapters_proc_dir);
343		bdp->proc_parent = NULL;
344	}
345
346	remove_proc_entry(info, adapters_proc_dir);
347
348	/* try to remove the main /proc dir, if it's empty */
349	e100_proc_cleanup();
350}
351
352int
353e100_create_proc_subdir(struct e100_private *bdp)
354{
355	struct proc_dir_entry *dev_dir;
356	e100_proc_entry *pe;
357	char info[256];
358	int len;
359	void *data;
360
361	/* create the main /proc dir if needed */
362	if (!adapters_proc_dir) {
363		if (!e100_init_proc_dir())
364			return -ENOMEM;
365	}
366
367	strncpy(info, bdp->ifname, sizeof (info));
368	len = strlen(info);
369	strncat(info + len, ".info", sizeof (info) - len);
370
371	/* info */
372	if (!(create_proc_rw(info, bdp, adapters_proc_dir, read_info, 0))) {
373		e100_proc_cleanup();
374		return -ENOMEM;
375	}
376
377	dev_dir = create_proc_entry(bdp->ifname, S_IFDIR,
378				    adapters_proc_dir);
379	bdp->proc_parent = dev_dir;
380
381	if (!dev_dir) {
382		e100_remove_proc_subdir(bdp, bdp->ifname);
383		return -ENOMEM;
384	}
385
386	for (pe = e100_proc_list; pe->name[0]; pe++) {
387		if (pe->name[0] == '\n')
388			continue;
389
390		if (pe->offset != ~0)
391			data = ((char *) bdp) + pe->offset;
392		else
393			data = NULL;
394
395		if (!(create_proc_rw(pe->name, data, dev_dir,
396				     pe->read_proc, pe->write_proc))) {
397			e100_remove_proc_subdir(bdp, bdp->ifname);
398			return -ENOMEM;
399		}
400	}
401
402	return 0;
403}
404
405/****************************************************************************
406 * Name:          e100_init_proc_dir
407 *
408 * Description:   This routine creates the top-level /proc directory for the
409 *                driver in /proc/net
410 *
411 * Arguments:     none
412 *
413 * Returns:       true on success, false on fail
414 *
415 ***************************************************************************/
416static unsigned char
417e100_init_proc_dir(void)
418{
419	int len;
420
421	/* first check if adapters_proc_dir already exists */
422	len = strlen(ADAPTERS_PROC_DIR);
423	for (adapters_proc_dir = proc_net->subdir;
424	     adapters_proc_dir; adapters_proc_dir = adapters_proc_dir->next) {
425
426		if ((adapters_proc_dir->namelen == len) &&
427		    (!memcmp(adapters_proc_dir->name, ADAPTERS_PROC_DIR, len)))
428			break;
429	}
430
431	if (!adapters_proc_dir)
432		adapters_proc_dir =
433			create_proc_entry(ADAPTERS_PROC_DIR, S_IFDIR, proc_net);
434
435	if (!adapters_proc_dir)
436		return false;
437
438	return true;
439}
440
441/****************************************************************************
442 * Name:          e100_proc_cleanup
443 *
444 * Description:   This routine clears the top-level /proc directory, if empty.
445 *
446 * Arguments:     none
447 *
448 * Returns:       none
449 *
450 ***************************************************************************/
451static void
452e100_proc_cleanup(void)
453{
454	struct proc_dir_entry *de;
455
456	if (adapters_proc_dir == NULL) {
457		return;
458	}
459
460	/* check if subdir list is empty before removing adapters_proc_dir */
461	for (de = adapters_proc_dir->subdir; de; de = de->next) {
462		/* ignore . and .. */
463		if (*(de->name) != '.')
464			break;
465	}
466
467	if (de)
468		return;
469
470	remove_proc_entry(ADAPTERS_PROC_DIR, proc_net);
471	adapters_proc_dir = NULL;
472}
473
474#endif /* CONFIG_PROC_FS */
475