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