| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /**************************************************************************** | 
|  | 2 | * sdlamain.c	WANPIPE(tm) Multiprotocol WAN Link Driver.  Main module. | 
|  | 3 | * | 
|  | 4 | * Author:	Nenad Corbic	<ncorbic@sangoma.com> | 
|  | 5 | *		Gideon Hack | 
|  | 6 | * | 
|  | 7 | * Copyright:	(c) 1995-2000 Sangoma Technologies Inc. | 
|  | 8 | * | 
|  | 9 | *		This program is free software; you can redistribute it and/or | 
|  | 10 | *		modify it under the terms of the GNU General Public License | 
|  | 11 | *		as published by the Free Software Foundation; either version | 
|  | 12 | *		2 of the License, or (at your option) any later version. | 
|  | 13 | * ============================================================================ | 
|  | 14 | * Dec 22, 2000  Nenad Corbic	Updated for 2.4.X kernels. | 
|  | 15 | * 				Removed the polling routine. | 
|  | 16 | * Nov 13, 2000  Nenad Corbic	Added hw probing on module load and dynamic | 
|  | 17 | * 				device allocation. | 
|  | 18 | * Nov 7,  2000  Nenad Corbic	Fixed the Multi-Port PPP for kernels | 
|  | 19 | *                               2.2.16 and above. | 
|  | 20 | * Aug 2,  2000  Nenad Corbic	Block the Multi-Port PPP from running on | 
|  | 21 | *  			        kernels 2.2.16 or greater.  The SyncPPP | 
|  | 22 | *  			        has changed. | 
|  | 23 | * Jul 25, 2000  Nenad Corbic	Updated the Piggiback support for MultPPPP. | 
|  | 24 | * Jul 13, 2000	Nenad Corbic	Added Multi-PPP support. | 
|  | 25 | * Feb 02, 2000  Nenad Corbic    Fixed up piggyback probing and selection. | 
|  | 26 | * Sep 23, 1999  Nenad Corbic    Added support for SMP | 
|  | 27 | * Sep 13, 1999  Nenad Corbic	Each port is treated as a separate device. | 
|  | 28 | * Jun 02, 1999  Gideon Hack     Added support for the S514 adapter. | 
|  | 29 | *				Updates for Linux 2.2.X kernels. | 
|  | 30 | * Sep 17, 1998	Jaspreet Singh	Updated for 2.1.121+ kernel | 
|  | 31 | * Nov 28, 1997	Jaspreet Singh	Changed DRV_RELEASE to 1 | 
|  | 32 | * Nov 10, 1997	Jaspreet Singh	Changed sti() to restore_flags(); | 
|  | 33 | * Nov 06, 1997 	Jaspreet Singh	Changed DRV_VERSION to 4 and DRV_RELEASE to 0 | 
|  | 34 | * Oct 20, 1997 	Jaspreet Singh	Modified sdla_isr routine so that card->in_isr | 
|  | 35 | *				assignments are taken out and placed in the | 
|  | 36 | *				sdla_ppp.c, sdla_fr.c and sdla_x25.c isr | 
|  | 37 | *				routines. Took out 'wandev->tx_int_enabled' and | 
|  | 38 | *				replaced it with 'wandev->enable_tx_int'. | 
|  | 39 | * May 29, 1997	Jaspreet Singh	Flow Control Problem | 
|  | 40 | *				added "wandev->tx_int_enabled=1" line in the | 
|  | 41 | *				init module. This line initializes the flag for | 
|  | 42 | *				preventing Interrupt disabled with device set to | 
|  | 43 | *				busy | 
|  | 44 | * Jan 15, 1997	Gene Kozin	Version 3.1.0 | 
|  | 45 | *				 o added UDP management stuff | 
|  | 46 | * Jan 02, 1997	Gene Kozin	Initial version. | 
|  | 47 | *****************************************************************************/ | 
|  | 48 |  | 
|  | 49 | #include <linux/config.h>	/* OS configuration options */ | 
|  | 50 | #include <linux/stddef.h>	/* offsetof(), etc. */ | 
|  | 51 | #include <linux/errno.h>	/* return codes */ | 
|  | 52 | #include <linux/string.h>	/* inline memset(), etc. */ | 
|  | 53 | #include <linux/init.h> | 
|  | 54 | #include <linux/slab.h>	/* kmalloc(), kfree() */ | 
|  | 55 | #include <linux/kernel.h>	/* printk(), and other useful stuff */ | 
|  | 56 | #include <linux/module.h>	/* support for loadable modules */ | 
|  | 57 | #include <linux/ioport.h>	/* request_region(), release_region() */ | 
|  | 58 | #include <linux/wanrouter.h>	/* WAN router definitions */ | 
|  | 59 | #include <linux/wanpipe.h>	/* WANPIPE common user API definitions */ | 
|  | 60 |  | 
|  | 61 | #include <linux/in.h> | 
|  | 62 | #include <asm/io.h>		/* phys_to_virt() */ | 
|  | 63 | #include <linux/pci.h> | 
|  | 64 | #include <linux/sdlapci.h> | 
|  | 65 | #include <linux/if_wanpipe_common.h> | 
|  | 66 |  | 
|  | 67 | #include <asm/uaccess.h>	/* kernel <-> user copy */ | 
|  | 68 | #include <linux/inetdevice.h> | 
|  | 69 |  | 
|  | 70 | #include <linux/ip.h> | 
|  | 71 | #include <net/route.h> | 
|  | 72 |  | 
|  | 73 | #define KMEM_SAFETYZONE 8 | 
|  | 74 |  | 
|  | 75 |  | 
|  | 76 | #ifndef CONFIG_WANPIPE_FR | 
|  | 77 | #define wpf_init(a,b) (-EPROTONOSUPPORT) | 
|  | 78 | #endif | 
|  | 79 |  | 
|  | 80 | #ifndef CONFIG_WANPIPE_CHDLC | 
|  | 81 | #define wpc_init(a,b) (-EPROTONOSUPPORT) | 
|  | 82 | #endif | 
|  | 83 |  | 
|  | 84 | #ifndef CONFIG_WANPIPE_X25 | 
|  | 85 | #define wpx_init(a,b) (-EPROTONOSUPPORT) | 
|  | 86 | #endif | 
|  | 87 |  | 
|  | 88 | #ifndef CONFIG_WANPIPE_PPP | 
|  | 89 | #define wpp_init(a,b) (-EPROTONOSUPPORT) | 
|  | 90 | #endif | 
|  | 91 |  | 
|  | 92 | #ifndef CONFIG_WANPIPE_MULTPPP | 
|  | 93 | #define wsppp_init(a,b) (-EPROTONOSUPPORT) | 
|  | 94 | #endif | 
|  | 95 |  | 
|  | 96 |  | 
|  | 97 | /***********FOR DEBUGGING PURPOSES********************************************* | 
|  | 98 | static void * dbg_kmalloc(unsigned int size, int prio, int line) { | 
|  | 99 | int i = 0; | 
|  | 100 | void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio); | 
|  | 101 | char * c1 = v; | 
|  | 102 | c1 += sizeof(unsigned int); | 
|  | 103 | *((unsigned int *)v) = size; | 
|  | 104 |  | 
|  | 105 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | 
|  | 106 | c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D'; | 
|  | 107 | c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F'; | 
|  | 108 | c1 += 8; | 
|  | 109 | } | 
|  | 110 | c1 += size; | 
|  | 111 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | 
|  | 112 | c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G'; | 
|  | 113 | c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L'; | 
|  | 114 | c1 += 8; | 
|  | 115 | } | 
|  | 116 | v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8; | 
|  | 117 | printk(KERN_INFO "line %d  kmalloc(%d,%d) = %p\n",line,size,prio,v); | 
|  | 118 | return v; | 
|  | 119 | } | 
|  | 120 | static void dbg_kfree(void * v, int line) { | 
|  | 121 | unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8)); | 
|  | 122 | unsigned int size = *sp; | 
|  | 123 | char * c1 = ((char *)v) - KMEM_SAFETYZONE*8; | 
|  | 124 | int i = 0; | 
|  | 125 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | 
|  | 126 | if (   c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D' | 
|  | 127 | || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') { | 
|  | 128 | printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v); | 
|  | 129 | printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8, | 
|  | 130 | c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] ); | 
|  | 131 | } | 
|  | 132 | c1 += 8; | 
|  | 133 | } | 
|  | 134 | c1 += size; | 
|  | 135 | for (i = 0; i < KMEM_SAFETYZONE; i++) { | 
|  | 136 | if (   c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G' | 
|  | 137 | || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L' | 
|  | 138 | ) { | 
|  | 139 | printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v); | 
|  | 140 | printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8, | 
|  | 141 | c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] ); | 
|  | 142 | } | 
|  | 143 | c1 += 8; | 
|  | 144 | } | 
|  | 145 | printk(KERN_INFO "line %d  kfree(%p)\n",line,v); | 
|  | 146 | v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8); | 
|  | 147 | kfree(v); | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | #define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__) | 
|  | 151 | #define kfree(x) dbg_kfree(x,__LINE__) | 
|  | 152 | ******************************************************************************/ | 
|  | 153 |  | 
|  | 154 |  | 
|  | 155 |  | 
|  | 156 | /****** Defines & Macros ****************************************************/ | 
|  | 157 |  | 
|  | 158 | #ifdef	_DEBUG_ | 
|  | 159 | #define	STATIC | 
|  | 160 | #else | 
|  | 161 | #define	STATIC		static | 
|  | 162 | #endif | 
|  | 163 |  | 
|  | 164 | #define	DRV_VERSION	5		/* version number */ | 
|  | 165 | #define	DRV_RELEASE	0		/* release (minor version) number */ | 
|  | 166 | #define	MAX_CARDS	16		/* max number of adapters */ | 
|  | 167 |  | 
|  | 168 | #ifndef	CONFIG_WANPIPE_CARDS		/* configurable option */ | 
|  | 169 | #define	CONFIG_WANPIPE_CARDS 1 | 
|  | 170 | #endif | 
|  | 171 |  | 
|  | 172 | #define	CMD_OK		0		/* normal firmware return code */ | 
|  | 173 | #define	CMD_TIMEOUT	0xFF		/* firmware command timed out */ | 
|  | 174 | #define	MAX_CMD_RETRY	10		/* max number of firmware retries */ | 
|  | 175 | /****** Function Prototypes *************************************************/ | 
|  | 176 |  | 
|  | 177 | extern void disable_irq(unsigned int); | 
|  | 178 | extern void enable_irq(unsigned int); | 
|  | 179 |  | 
|  | 180 | /* WAN link driver entry points */ | 
|  | 181 | static int setup(struct wan_device* wandev, wandev_conf_t* conf); | 
|  | 182 | static int shutdown(struct wan_device* wandev); | 
|  | 183 | static int ioctl(struct wan_device* wandev, unsigned cmd, unsigned long arg); | 
|  | 184 |  | 
|  | 185 | /* IOCTL handlers */ | 
|  | 186 | static int ioctl_dump	(sdla_t* card, sdla_dump_t* u_dump); | 
|  | 187 | static int ioctl_exec	(sdla_t* card, sdla_exec_t* u_exec, int); | 
|  | 188 |  | 
|  | 189 | /* Miscellaneous functions */ | 
|  | 190 | STATIC irqreturn_t sdla_isr	(int irq, void* dev_id, struct pt_regs *regs); | 
|  | 191 | static void release_hw  (sdla_t *card); | 
|  | 192 |  | 
|  | 193 | static int check_s508_conflicts (sdla_t* card,wandev_conf_t* conf, int*); | 
|  | 194 | static int check_s514_conflicts (sdla_t* card,wandev_conf_t* conf, int*); | 
|  | 195 |  | 
|  | 196 |  | 
|  | 197 | /****** Global Data ********************************************************** | 
|  | 198 | * Note: All data must be explicitly initialized!!! | 
|  | 199 | */ | 
|  | 200 |  | 
|  | 201 | /* private data */ | 
|  | 202 | static char drvname[]	= "wanpipe"; | 
|  | 203 | static char fullname[]	= "WANPIPE(tm) Multiprotocol Driver"; | 
|  | 204 | static char copyright[]	= "(c) 1995-2000 Sangoma Technologies Inc."; | 
|  | 205 | static int ncards; | 
|  | 206 | static sdla_t* card_array;		/* adapter data space */ | 
|  | 207 |  | 
|  | 208 | /* Wanpipe's own workqueue, used for all API's. | 
|  | 209 | * All protocol specific tasks will be inserted | 
|  | 210 | * into the "wanpipe_wq" workqueue. | 
|  | 211 |  | 
|  | 212 | * The kernel workqueue mechanism will execute | 
|  | 213 | * all pending tasks in the "wanpipe_wq" workqueue. | 
|  | 214 | */ | 
|  | 215 |  | 
|  | 216 | struct workqueue_struct *wanpipe_wq; | 
|  | 217 | DECLARE_WORK(wanpipe_work, NULL, NULL); | 
|  | 218 |  | 
|  | 219 | static int wanpipe_bh_critical; | 
|  | 220 |  | 
|  | 221 | /******* Kernel Loadable Module Entry Points ********************************/ | 
|  | 222 |  | 
|  | 223 | /*============================================================================ | 
|  | 224 | * Module 'insert' entry point. | 
|  | 225 | * o print announcement | 
|  | 226 | * o allocate adapter data space | 
|  | 227 | * o initialize static data | 
|  | 228 | * o register all cards with WAN router | 
|  | 229 | * o calibrate SDLA shared memory access delay. | 
|  | 230 | * | 
|  | 231 | * Return:	0	Ok | 
|  | 232 | *		< 0	error. | 
|  | 233 | * Context:	process | 
|  | 234 | */ | 
|  | 235 |  | 
|  | 236 | static int __init wanpipe_init(void) | 
|  | 237 | { | 
|  | 238 | int cnt, err = 0; | 
|  | 239 |  | 
|  | 240 | printk(KERN_INFO "%s v%u.%u %s\n", | 
|  | 241 | fullname, DRV_VERSION, DRV_RELEASE, copyright); | 
|  | 242 |  | 
|  | 243 | wanpipe_wq = create_workqueue("wanpipe_wq"); | 
|  | 244 | if (!wanpipe_wq) | 
|  | 245 | return -ENOMEM; | 
|  | 246 |  | 
|  | 247 | /* Probe for wanpipe cards and return the number found */ | 
|  | 248 | printk(KERN_INFO "wanpipe: Probing for WANPIPE hardware.\n"); | 
|  | 249 | ncards = wanpipe_hw_probe(); | 
|  | 250 | if (ncards){ | 
|  | 251 | printk(KERN_INFO "wanpipe: Allocating maximum %i devices: wanpipe%i - wanpipe%i.\n",ncards,1,ncards); | 
|  | 252 | }else{ | 
|  | 253 | printk(KERN_INFO "wanpipe: No S514/S508 cards found, unloading modules!\n"); | 
|  | 254 | destroy_workqueue(wanpipe_wq); | 
|  | 255 | return -ENODEV; | 
|  | 256 | } | 
|  | 257 |  | 
|  | 258 | /* Verify number of cards and allocate adapter data space */ | 
|  | 259 | card_array = kmalloc(sizeof(sdla_t) * ncards, GFP_KERNEL); | 
|  | 260 | if (card_array == NULL) { | 
|  | 261 | destroy_workqueue(wanpipe_wq); | 
|  | 262 | return -ENOMEM; | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | memset(card_array, 0, sizeof(sdla_t) * ncards); | 
|  | 266 |  | 
|  | 267 | /* Register adapters with WAN router */ | 
|  | 268 | for (cnt = 0; cnt < ncards; ++ cnt) { | 
|  | 269 | sdla_t* card = &card_array[cnt]; | 
|  | 270 | struct wan_device* wandev = &card->wandev; | 
|  | 271 |  | 
|  | 272 | card->next = NULL; | 
|  | 273 | sprintf(card->devname, "%s%d", drvname, cnt + 1); | 
|  | 274 | wandev->magic    = ROUTER_MAGIC; | 
|  | 275 | wandev->name     = card->devname; | 
|  | 276 | wandev->private  = card; | 
|  | 277 | wandev->enable_tx_int = 0; | 
|  | 278 | wandev->setup    = &setup; | 
|  | 279 | wandev->shutdown = &shutdown; | 
|  | 280 | wandev->ioctl    = &ioctl; | 
|  | 281 | err = register_wan_device(wandev); | 
|  | 282 | if (err) { | 
|  | 283 | printk(KERN_INFO | 
|  | 284 | "%s: %s registration failed with error %d!\n", | 
|  | 285 | drvname, card->devname, err); | 
|  | 286 | break; | 
|  | 287 | } | 
|  | 288 | } | 
|  | 289 | if (cnt){ | 
|  | 290 | ncards = cnt;	/* adjust actual number of cards */ | 
|  | 291 | }else { | 
|  | 292 | kfree(card_array); | 
|  | 293 | destroy_workqueue(wanpipe_wq); | 
|  | 294 | printk(KERN_INFO "IN Init Module: NO Cards registered\n"); | 
|  | 295 | err = -ENODEV; | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | return err; | 
|  | 299 | } | 
|  | 300 |  | 
|  | 301 | /*============================================================================ | 
|  | 302 | * Module 'remove' entry point. | 
|  | 303 | * o unregister all adapters from the WAN router | 
|  | 304 | * o release all remaining system resources | 
|  | 305 | */ | 
|  | 306 | static void __exit wanpipe_cleanup(void) | 
|  | 307 | { | 
|  | 308 | int i; | 
|  | 309 |  | 
|  | 310 | if (!ncards) | 
|  | 311 | return; | 
|  | 312 |  | 
|  | 313 | for (i = 0; i < ncards; ++i) { | 
|  | 314 | sdla_t* card = &card_array[i]; | 
|  | 315 | unregister_wan_device(card->devname); | 
|  | 316 | } | 
|  | 317 | destroy_workqueue(wanpipe_wq); | 
|  | 318 | kfree(card_array); | 
|  | 319 |  | 
|  | 320 | printk(KERN_INFO "\nwanpipe: WANPIPE Modules Unloaded.\n"); | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | module_init(wanpipe_init); | 
|  | 324 | module_exit(wanpipe_cleanup); | 
|  | 325 |  | 
|  | 326 | /******* WAN Device Driver Entry Points *************************************/ | 
|  | 327 |  | 
|  | 328 | /*============================================================================ | 
|  | 329 | * Setup/configure WAN link driver. | 
|  | 330 | * o check adapter state | 
|  | 331 | * o make sure firmware is present in configuration | 
|  | 332 | * o make sure I/O port and IRQ are specified | 
|  | 333 | * o make sure I/O region is available | 
|  | 334 | * o allocate interrupt vector | 
|  | 335 | * o setup SDLA hardware | 
|  | 336 | * o call appropriate routine to perform protocol-specific initialization | 
|  | 337 | * o mark I/O region as used | 
|  | 338 | * o if this is the first active card, then schedule background task | 
|  | 339 | * | 
|  | 340 | * This function is called when router handles ROUTER_SETUP IOCTL. The | 
|  | 341 | * configuration structure is in kernel memory (including extended data, if | 
|  | 342 | * any). | 
|  | 343 | */ | 
|  | 344 |  | 
|  | 345 | static int setup(struct wan_device* wandev, wandev_conf_t* conf) | 
|  | 346 | { | 
|  | 347 | sdla_t* card; | 
|  | 348 | int err = 0; | 
|  | 349 | int irq=0; | 
|  | 350 |  | 
|  | 351 | /* Sanity checks */ | 
|  | 352 | if ((wandev == NULL) || (wandev->private == NULL) || (conf == NULL)){ | 
|  | 353 | printk(KERN_INFO | 
|  | 354 | "%s: Failed Sdlamain Setup wandev %u, card %u, conf %u !\n", | 
|  | 355 | wandev->name, | 
|  | 356 | (unsigned int)wandev,(unsigned int)wandev->private, | 
|  | 357 | (unsigned int)conf); | 
|  | 358 | return -EFAULT; | 
|  | 359 | } | 
|  | 360 |  | 
|  | 361 | printk(KERN_INFO "%s: Starting WAN Setup\n", wandev->name); | 
|  | 362 |  | 
|  | 363 | card = wandev->private; | 
|  | 364 | if (wandev->state != WAN_UNCONFIGURED){ | 
|  | 365 | printk(KERN_INFO "%s: failed sdlamain setup, busy!\n", | 
|  | 366 | wandev->name); | 
|  | 367 | return -EBUSY;		/* already configured */ | 
|  | 368 | } | 
|  | 369 |  | 
|  | 370 | printk(KERN_INFO "\nProcessing WAN device %s...\n", wandev->name); | 
|  | 371 |  | 
|  | 372 | /* Initialize the counters for each wandev | 
|  | 373 | * Used for counting number of times new_if and | 
|  | 374 | * del_if get called. | 
|  | 375 | */ | 
|  | 376 | wandev->del_if_cnt = 0; | 
|  | 377 | wandev->new_if_cnt = 0; | 
|  | 378 | wandev->config_id  = conf->config_id; | 
|  | 379 |  | 
|  | 380 | if (!conf->data_size || (conf->data == NULL)) { | 
|  | 381 | printk(KERN_INFO | 
|  | 382 | "%s: firmware not found in configuration data!\n", | 
|  | 383 | wandev->name); | 
|  | 384 | return -EINVAL; | 
|  | 385 | } | 
|  | 386 |  | 
|  | 387 | /* Check for resource conflicts and setup the | 
|  | 388 | * card for piggibacking if necessary */ | 
|  | 389 | if(!conf->S514_CPU_no[0]) { | 
|  | 390 | if ((err=check_s508_conflicts(card,conf,&irq)) != 0){ | 
|  | 391 | return err; | 
|  | 392 | } | 
|  | 393 | }else { | 
|  | 394 | if ((err=check_s514_conflicts(card,conf,&irq)) != 0){ | 
|  | 395 | return err; | 
|  | 396 | } | 
|  | 397 | } | 
|  | 398 |  | 
|  | 399 | /* If the current card has already been configured | 
|  | 400 | * or it's a piggyback card, do not try to allocate | 
|  | 401 | * resources. | 
|  | 402 | */ | 
|  | 403 | if (!card->wandev.piggyback && !card->configured){ | 
|  | 404 |  | 
|  | 405 | /* Configure hardware, load firmware, etc. */ | 
|  | 406 | memset(&card->hw, 0, sizeof(sdlahw_t)); | 
|  | 407 |  | 
|  | 408 | /* for an S514 adapter, pass the CPU number and the slot number read */ | 
|  | 409 | /* from 'router.conf' to the 'sdla_setup()' function via the 'port' */ | 
|  | 410 | /* parameter */ | 
|  | 411 | if (conf->S514_CPU_no[0]){ | 
|  | 412 |  | 
|  | 413 | card->hw.S514_cpu_no[0] = conf->S514_CPU_no[0]; | 
|  | 414 | card->hw.S514_slot_no = conf->PCI_slot_no; | 
|  | 415 | card->hw.auto_pci_cfg = conf->auto_pci_cfg; | 
|  | 416 |  | 
|  | 417 | if (card->hw.auto_pci_cfg == WANOPT_YES){ | 
|  | 418 | printk(KERN_INFO "%s: Setting CPU to %c and Slot to Auto\n", | 
|  | 419 | card->devname, card->hw.S514_cpu_no[0]); | 
|  | 420 | }else{ | 
|  | 421 | printk(KERN_INFO "%s: Setting CPU to %c and Slot to %i\n", | 
|  | 422 | card->devname, card->hw.S514_cpu_no[0], card->hw.S514_slot_no); | 
|  | 423 | } | 
|  | 424 |  | 
|  | 425 | }else{ | 
|  | 426 | /* 508 Card io port and irq initialization */ | 
|  | 427 | card->hw.port = conf->ioport; | 
|  | 428 | card->hw.irq = (conf->irq == 9) ? 2 : conf->irq; | 
|  | 429 | } | 
|  | 430 |  | 
|  | 431 |  | 
|  | 432 | /* Compute the virtual address of the card in kernel space */ | 
|  | 433 | if(conf->maddr){ | 
|  | 434 | card->hw.dpmbase = phys_to_virt(conf->maddr); | 
|  | 435 | }else{ | 
|  | 436 | card->hw.dpmbase = (void *)conf->maddr; | 
|  | 437 | } | 
|  | 438 |  | 
|  | 439 | card->hw.dpmsize = SDLA_WINDOWSIZE; | 
|  | 440 |  | 
|  | 441 | /* set the adapter type if using an S514 adapter */ | 
|  | 442 | card->hw.type = (conf->S514_CPU_no[0]) ? SDLA_S514 : conf->hw_opt[0]; | 
|  | 443 | card->hw.pclk = conf->hw_opt[1]; | 
|  | 444 |  | 
|  | 445 | err = sdla_setup(&card->hw, conf->data, conf->data_size); | 
|  | 446 | if (err){ | 
|  | 447 | printk(KERN_INFO "%s: Hardware setup Failed %i\n", | 
|  | 448 | card->devname,err); | 
|  | 449 | return err; | 
|  | 450 | } | 
|  | 451 |  | 
|  | 452 | if(card->hw.type != SDLA_S514) | 
|  | 453 | irq = (conf->irq == 2) ? 9 : conf->irq; /* IRQ2 -> IRQ9 */ | 
|  | 454 | else | 
|  | 455 | irq = card->hw.irq; | 
|  | 456 |  | 
|  | 457 | /* request an interrupt vector - note that interrupts may be shared */ | 
|  | 458 | /* when using the S514 PCI adapter */ | 
|  | 459 |  | 
|  | 460 | if(request_irq(irq, sdla_isr, | 
|  | 461 | (card->hw.type == SDLA_S514) ? SA_SHIRQ : 0, | 
|  | 462 | wandev->name, card)){ | 
|  | 463 |  | 
|  | 464 | printk(KERN_INFO "%s: Can't reserve IRQ %d!\n", wandev->name, irq); | 
|  | 465 | return -EINVAL; | 
|  | 466 | } | 
|  | 467 |  | 
|  | 468 | }else{ | 
|  | 469 | printk(KERN_INFO "%s: Card Configured %lu or Piggybacking %i!\n", | 
|  | 470 | wandev->name,card->configured,card->wandev.piggyback); | 
|  | 471 | } | 
|  | 472 |  | 
|  | 473 |  | 
|  | 474 | if (!card->configured){ | 
|  | 475 |  | 
|  | 476 | /* Initialize the Spin lock */ | 
|  | 477 | printk(KERN_INFO "%s: Initializing for SMP\n",wandev->name); | 
|  | 478 |  | 
|  | 479 | /* Piggyback spin lock has already been initialized, | 
|  | 480 | * in check_s514/s508_conflicts() */ | 
|  | 481 | if (!card->wandev.piggyback){ | 
|  | 482 | spin_lock_init(&card->wandev.lock); | 
|  | 483 | } | 
|  | 484 |  | 
|  | 485 | /* Intialize WAN device data space */ | 
|  | 486 | wandev->irq       = irq; | 
|  | 487 | wandev->dma       = 0; | 
|  | 488 | if(card->hw.type != SDLA_S514){ | 
|  | 489 | wandev->ioport = card->hw.port; | 
|  | 490 | }else{ | 
|  | 491 | wandev->S514_cpu_no[0] = card->hw.S514_cpu_no[0]; | 
|  | 492 | wandev->S514_slot_no = card->hw.S514_slot_no; | 
|  | 493 | } | 
|  | 494 | wandev->maddr     = (unsigned long)card->hw.dpmbase; | 
|  | 495 | wandev->msize     = card->hw.dpmsize; | 
|  | 496 | wandev->hw_opt[0] = card->hw.type; | 
|  | 497 | wandev->hw_opt[1] = card->hw.pclk; | 
|  | 498 | wandev->hw_opt[2] = card->hw.memory; | 
|  | 499 | wandev->hw_opt[3] = card->hw.fwid; | 
|  | 500 | } | 
|  | 501 |  | 
|  | 502 | /* Protocol-specific initialization */ | 
|  | 503 | switch (card->hw.fwid) { | 
|  | 504 |  | 
|  | 505 | case SFID_X25_502: | 
|  | 506 | case SFID_X25_508: | 
|  | 507 | printk(KERN_INFO "%s: Starting X.25 Protocol Init.\n", | 
|  | 508 | card->devname); | 
|  | 509 | err = wpx_init(card, conf); | 
|  | 510 | break; | 
|  | 511 | case SFID_FR502: | 
|  | 512 | case SFID_FR508: | 
|  | 513 | printk(KERN_INFO "%s: Starting Frame Relay Protocol Init.\n", | 
|  | 514 | card->devname); | 
|  | 515 | err = wpf_init(card, conf); | 
|  | 516 | break; | 
|  | 517 | case SFID_PPP502: | 
|  | 518 | case SFID_PPP508: | 
|  | 519 | printk(KERN_INFO "%s: Starting PPP Protocol Init.\n", | 
|  | 520 | card->devname); | 
|  | 521 | err = wpp_init(card, conf); | 
|  | 522 | break; | 
|  | 523 |  | 
|  | 524 | case SFID_CHDLC508: | 
|  | 525 | case SFID_CHDLC514: | 
|  | 526 | if (conf->ft1){ | 
|  | 527 | printk(KERN_INFO "%s: Starting FT1 CSU/DSU Config Driver.\n", | 
|  | 528 | card->devname); | 
|  | 529 | err = wpft1_init(card, conf); | 
|  | 530 | break; | 
|  | 531 |  | 
|  | 532 | }else if (conf->config_id == WANCONFIG_MPPP){ | 
|  | 533 | printk(KERN_INFO "%s: Starting Multi-Port PPP Protocol Init.\n", | 
|  | 534 | card->devname); | 
|  | 535 | err = wsppp_init(card,conf); | 
|  | 536 | break; | 
|  | 537 |  | 
|  | 538 | }else{ | 
|  | 539 | printk(KERN_INFO "%s: Starting CHDLC Protocol Init.\n", | 
|  | 540 | card->devname); | 
|  | 541 | err = wpc_init(card, conf); | 
|  | 542 | break; | 
|  | 543 | } | 
|  | 544 | default: | 
|  | 545 | printk(KERN_INFO "%s: Error, Firmware is not supported %X %X!\n", | 
|  | 546 | wandev->name,card->hw.fwid,SFID_CHDLC508); | 
|  | 547 | err = -EPROTONOSUPPORT; | 
|  | 548 | } | 
|  | 549 |  | 
|  | 550 | if (err != 0){ | 
|  | 551 | if (err == -EPROTONOSUPPORT){ | 
|  | 552 | printk(KERN_INFO | 
|  | 553 | "%s: Error, Protocol selected has not been compiled!\n", | 
|  | 554 | card->devname); | 
|  | 555 | printk(KERN_INFO | 
|  | 556 | "%s:        Re-configure the kernel and re-build the modules!\n", | 
|  | 557 | card->devname); | 
|  | 558 | } | 
|  | 559 |  | 
|  | 560 | release_hw(card); | 
|  | 561 | wandev->state = WAN_UNCONFIGURED; | 
|  | 562 | return err; | 
|  | 563 | } | 
|  | 564 |  | 
|  | 565 |  | 
|  | 566 | /* Reserve I/O region and schedule background task */ | 
|  | 567 | if(card->hw.type != SDLA_S514 && !card->wandev.piggyback) | 
|  | 568 | if (!request_region(card->hw.port, card->hw.io_range, | 
|  | 569 | wandev->name)) { | 
|  | 570 | printk(KERN_WARNING "port 0x%04x busy\n", card->hw.port); | 
|  | 571 | release_hw(card); | 
|  | 572 | wandev->state = WAN_UNCONFIGURED; | 
|  | 573 | return -EBUSY; | 
|  | 574 | } | 
|  | 575 |  | 
|  | 576 | /* Only use the polling routine for the X25 protocol */ | 
|  | 577 |  | 
|  | 578 | card->wandev.critical=0; | 
|  | 579 | return 0; | 
|  | 580 | } | 
|  | 581 |  | 
|  | 582 | /*================================================================== | 
|  | 583 | * configure_s508_card | 
|  | 584 | * | 
|  | 585 | * For a S508 adapter, check for a possible configuration error in that | 
|  | 586 | * we are loading an adapter in the same IO port as a previously loaded S508 | 
|  | 587 | * card. | 
|  | 588 | */ | 
|  | 589 |  | 
|  | 590 | static int check_s508_conflicts (sdla_t* card,wandev_conf_t* conf, int *irq) | 
|  | 591 | { | 
|  | 592 | unsigned long smp_flags; | 
|  | 593 | int i; | 
|  | 594 |  | 
|  | 595 | if (conf->ioport <= 0) { | 
|  | 596 | printk(KERN_INFO | 
|  | 597 | "%s: can't configure without I/O port address!\n", | 
|  | 598 | card->wandev.name); | 
|  | 599 | return -EINVAL; | 
|  | 600 | } | 
|  | 601 |  | 
|  | 602 | if (conf->irq <= 0) { | 
|  | 603 | printk(KERN_INFO "%s: can't configure without IRQ!\n", | 
|  | 604 | card->wandev.name); | 
|  | 605 | return -EINVAL; | 
|  | 606 | } | 
|  | 607 |  | 
|  | 608 | if (test_bit(0,&card->configured)) | 
|  | 609 | return 0; | 
|  | 610 |  | 
|  | 611 |  | 
|  | 612 | /* Check for already loaded card with the same IO port and IRQ | 
|  | 613 | * If found, copy its hardware configuration and use its | 
|  | 614 | * resources (i.e. piggybacking) | 
|  | 615 | */ | 
|  | 616 |  | 
|  | 617 | for (i = 0; i < ncards; i++) { | 
|  | 618 | sdla_t *nxt_card = &card_array[i]; | 
|  | 619 |  | 
|  | 620 | /* Skip the current card ptr */ | 
|  | 621 | if (nxt_card == card) | 
|  | 622 | continue; | 
|  | 623 |  | 
|  | 624 |  | 
|  | 625 | /* Find a card that is already configured with the | 
|  | 626 | * same IO Port */ | 
|  | 627 | if ((nxt_card->hw.type == SDLA_S508) && | 
|  | 628 | (nxt_card->hw.port == conf->ioport) && | 
|  | 629 | (nxt_card->next == NULL)){ | 
|  | 630 |  | 
|  | 631 | /* We found a card the card that has same configuration | 
|  | 632 | * as us. This means, that we must setup this card in | 
|  | 633 | * piggibacking mode. However, only CHDLC and MPPP protocol | 
|  | 634 | * support this setup */ | 
|  | 635 |  | 
|  | 636 | if ((conf->config_id == WANCONFIG_CHDLC || | 
|  | 637 | conf->config_id == WANCONFIG_MPPP) && | 
|  | 638 | (nxt_card->wandev.config_id == WANCONFIG_CHDLC || | 
|  | 639 | nxt_card->wandev.config_id == WANCONFIG_MPPP)){ | 
|  | 640 |  | 
|  | 641 | *irq = nxt_card->hw.irq; | 
|  | 642 | memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t)); | 
|  | 643 |  | 
|  | 644 | /* The master could already be running, we must | 
|  | 645 | * set this as a critical area */ | 
|  | 646 | lock_adapter_irq(&nxt_card->wandev.lock, &smp_flags); | 
|  | 647 |  | 
|  | 648 | nxt_card->next = card; | 
|  | 649 | card->next = nxt_card; | 
|  | 650 |  | 
|  | 651 | card->wandev.piggyback = WANOPT_YES; | 
|  | 652 |  | 
|  | 653 | /* We must initialise the piggiback spin lock here | 
|  | 654 | * since isr will try to lock card->next if it | 
|  | 655 | * exists */ | 
|  | 656 | spin_lock_init(&card->wandev.lock); | 
|  | 657 |  | 
|  | 658 | unlock_adapter_irq(&nxt_card->wandev.lock, &smp_flags); | 
|  | 659 | break; | 
|  | 660 | }else{ | 
|  | 661 | /* Trying to run piggibacking with a wrong protocol */ | 
|  | 662 | printk(KERN_INFO "%s: ERROR: Resource busy, ioport: 0x%x\n" | 
|  | 663 | "%s:        This protocol doesn't support\n" | 
|  | 664 | "%s:        multi-port operation!\n", | 
|  | 665 | card->devname,nxt_card->hw.port, | 
|  | 666 | card->devname,card->devname); | 
|  | 667 | return -EEXIST; | 
|  | 668 | } | 
|  | 669 | } | 
|  | 670 | } | 
|  | 671 |  | 
|  | 672 |  | 
|  | 673 | /* Make sure I/O port region is available only if we are the | 
|  | 674 | * master device.  If we are running in piggybacking mode, | 
|  | 675 | * we will use the resources of the master card. */ | 
|  | 676 | if (!card->wandev.piggyback) { | 
|  | 677 | struct resource *rr = | 
|  | 678 | request_region(conf->ioport, SDLA_MAXIORANGE, "sdlamain"); | 
|  | 679 | release_region(conf->ioport, SDLA_MAXIORANGE); | 
|  | 680 |  | 
|  | 681 | if (!rr) { | 
|  | 682 | printk(KERN_INFO | 
|  | 683 | "%s: I/O region 0x%X - 0x%X is in use!\n", | 
|  | 684 | card->wandev.name, conf->ioport, | 
|  | 685 | conf->ioport + SDLA_MAXIORANGE - 1); | 
|  | 686 | return -EINVAL; | 
|  | 687 | } | 
|  | 688 | } | 
|  | 689 |  | 
|  | 690 | return 0; | 
|  | 691 | } | 
|  | 692 |  | 
|  | 693 | /*================================================================== | 
|  | 694 | * configure_s514_card | 
|  | 695 | * | 
|  | 696 | * For a S514 adapter, check for a possible configuration error in that | 
|  | 697 | * we are loading an adapter in the same slot as a previously loaded S514 | 
|  | 698 | * card. | 
|  | 699 | */ | 
|  | 700 |  | 
|  | 701 |  | 
|  | 702 | static int check_s514_conflicts(sdla_t* card,wandev_conf_t* conf, int *irq) | 
|  | 703 | { | 
|  | 704 | unsigned long smp_flags; | 
|  | 705 | int i; | 
|  | 706 |  | 
|  | 707 | if (test_bit(0,&card->configured)) | 
|  | 708 | return 0; | 
|  | 709 |  | 
|  | 710 |  | 
|  | 711 | /* Check for already loaded card with the same IO port and IRQ | 
|  | 712 | * If found, copy its hardware configuration and use its | 
|  | 713 | * resources (i.e. piggybacking) | 
|  | 714 | */ | 
|  | 715 |  | 
|  | 716 | for (i = 0; i < ncards; i ++) { | 
|  | 717 |  | 
|  | 718 | sdla_t* nxt_card = &card_array[i]; | 
|  | 719 | if(nxt_card == card) | 
|  | 720 | continue; | 
|  | 721 |  | 
|  | 722 | if((nxt_card->hw.type == SDLA_S514) && | 
|  | 723 | (nxt_card->hw.S514_slot_no == conf->PCI_slot_no) && | 
|  | 724 | (nxt_card->hw.S514_cpu_no[0] == conf->S514_CPU_no[0])&& | 
|  | 725 | (nxt_card->next == NULL)){ | 
|  | 726 |  | 
|  | 727 |  | 
|  | 728 | if ((conf->config_id == WANCONFIG_CHDLC || | 
|  | 729 | conf->config_id == WANCONFIG_MPPP) && | 
|  | 730 | (nxt_card->wandev.config_id == WANCONFIG_CHDLC || | 
|  | 731 | nxt_card->wandev.config_id == WANCONFIG_MPPP)){ | 
|  | 732 |  | 
|  | 733 | *irq = nxt_card->hw.irq; | 
|  | 734 | memcpy(&card->hw, &nxt_card->hw, sizeof(sdlahw_t)); | 
|  | 735 |  | 
|  | 736 | /* The master could already be running, we must | 
|  | 737 | * set this as a critical area */ | 
|  | 738 | lock_adapter_irq(&nxt_card->wandev.lock,&smp_flags); | 
|  | 739 | nxt_card->next = card; | 
|  | 740 | card->next = nxt_card; | 
|  | 741 |  | 
|  | 742 | card->wandev.piggyback = WANOPT_YES; | 
|  | 743 |  | 
|  | 744 | /* We must initialise the piggiback spin lock here | 
|  | 745 | * since isr will try to lock card->next if it | 
|  | 746 | * exists */ | 
|  | 747 | spin_lock_init(&card->wandev.lock); | 
|  | 748 |  | 
|  | 749 | unlock_adapter_irq(&nxt_card->wandev.lock,&smp_flags); | 
|  | 750 |  | 
|  | 751 | }else{ | 
|  | 752 | /* Trying to run piggibacking with a wrong protocol */ | 
|  | 753 | printk(KERN_INFO "%s: ERROR: Resource busy: CPU %c PCISLOT %i\n" | 
|  | 754 | "%s:        This protocol doesn't support\n" | 
|  | 755 | "%s:        multi-port operation!\n", | 
|  | 756 | card->devname, | 
|  | 757 | conf->S514_CPU_no[0],conf->PCI_slot_no, | 
|  | 758 | card->devname,card->devname); | 
|  | 759 | return -EEXIST; | 
|  | 760 | } | 
|  | 761 | } | 
|  | 762 | } | 
|  | 763 |  | 
|  | 764 | return 0; | 
|  | 765 | } | 
|  | 766 |  | 
|  | 767 |  | 
|  | 768 |  | 
|  | 769 | /*============================================================================ | 
|  | 770 | * Shut down WAN link driver. | 
|  | 771 | * o shut down adapter hardware | 
|  | 772 | * o release system resources. | 
|  | 773 | * | 
|  | 774 | * This function is called by the router when device is being unregistered or | 
|  | 775 | * when it handles ROUTER_DOWN IOCTL. | 
|  | 776 | */ | 
|  | 777 | static int shutdown(struct wan_device* wandev) | 
|  | 778 | { | 
|  | 779 | sdla_t *card; | 
|  | 780 | int err=0; | 
|  | 781 |  | 
|  | 782 | /* sanity checks */ | 
|  | 783 | if ((wandev == NULL) || (wandev->private == NULL)){ | 
|  | 784 | return -EFAULT; | 
|  | 785 | } | 
|  | 786 |  | 
|  | 787 | if (wandev->state == WAN_UNCONFIGURED){ | 
|  | 788 | return 0; | 
|  | 789 | } | 
|  | 790 |  | 
|  | 791 | card = wandev->private; | 
|  | 792 |  | 
|  | 793 | if (card->tty_opt){ | 
|  | 794 | if (card->tty_open){ | 
|  | 795 | printk(KERN_INFO | 
|  | 796 | "%s: Shutdown Failed: TTY is still open\n", | 
|  | 797 | card->devname); | 
|  | 798 | return -EBUSY; | 
|  | 799 | } | 
|  | 800 | } | 
|  | 801 |  | 
|  | 802 | wandev->state = WAN_UNCONFIGURED; | 
|  | 803 |  | 
|  | 804 | set_bit(PERI_CRIT,(void*)&wandev->critical); | 
|  | 805 |  | 
|  | 806 | /* In case of piggibacking, make sure that | 
|  | 807 | * we never try to shutdown both devices at the same | 
|  | 808 | * time, because they depend on one another */ | 
|  | 809 |  | 
|  | 810 | if (card->disable_comm){ | 
|  | 811 | card->disable_comm(card); | 
|  | 812 | } | 
|  | 813 |  | 
|  | 814 | /* Release Resources */ | 
|  | 815 | release_hw(card); | 
|  | 816 |  | 
|  | 817 | /* only free the allocated I/O range if not an S514 adapter */ | 
|  | 818 | if (wandev->hw_opt[0] != SDLA_S514 && !card->configured){ | 
|  | 819 | release_region(card->hw.port, card->hw.io_range); | 
|  | 820 | } | 
|  | 821 |  | 
|  | 822 | if (!card->configured){ | 
|  | 823 | memset(&card->hw, 0, sizeof(sdlahw_t)); | 
|  | 824 | if (card->next){ | 
|  | 825 | memset(&card->next->hw, 0, sizeof(sdlahw_t)); | 
|  | 826 | } | 
|  | 827 | } | 
|  | 828 |  | 
|  | 829 |  | 
|  | 830 | clear_bit(PERI_CRIT,(void*)&wandev->critical); | 
|  | 831 | return err; | 
|  | 832 | } | 
|  | 833 |  | 
|  | 834 | static void release_hw (sdla_t *card) | 
|  | 835 | { | 
|  | 836 | sdla_t *nxt_card; | 
|  | 837 |  | 
|  | 838 |  | 
|  | 839 | /* Check if next device exists */ | 
|  | 840 | if (card->next){ | 
|  | 841 | nxt_card = card->next; | 
|  | 842 | /* If next device is down then release resources */ | 
|  | 843 | if (nxt_card->wandev.state == WAN_UNCONFIGURED){ | 
|  | 844 | if (card->wandev.piggyback){ | 
|  | 845 | /* If this device is piggyback then use | 
|  | 846 | * information of the master device | 
|  | 847 | */ | 
|  | 848 | printk(KERN_INFO "%s: Piggyback shutting down\n",card->devname); | 
|  | 849 | sdla_down(&card->next->hw); | 
|  | 850 | free_irq(card->wandev.irq, card->next); | 
|  | 851 | card->configured = 0; | 
|  | 852 | card->next->configured = 0; | 
|  | 853 | card->wandev.piggyback = 0; | 
|  | 854 | }else{ | 
|  | 855 | /* Master device shutting down */ | 
|  | 856 | printk(KERN_INFO "%s: Master shutting down\n",card->devname); | 
|  | 857 | sdla_down(&card->hw); | 
|  | 858 | free_irq(card->wandev.irq, card); | 
|  | 859 | card->configured = 0; | 
|  | 860 | card->next->configured = 0; | 
|  | 861 | } | 
|  | 862 | }else{ | 
|  | 863 | printk(KERN_INFO "%s: Device still running %i\n", | 
|  | 864 | nxt_card->devname,nxt_card->wandev.state); | 
|  | 865 |  | 
|  | 866 | card->configured = 1; | 
|  | 867 | } | 
|  | 868 | }else{ | 
|  | 869 | printk(KERN_INFO "%s: Master shutting down\n",card->devname); | 
|  | 870 | sdla_down(&card->hw); | 
|  | 871 | free_irq(card->wandev.irq, card); | 
|  | 872 | card->configured = 0; | 
|  | 873 | } | 
|  | 874 | return; | 
|  | 875 | } | 
|  | 876 |  | 
|  | 877 |  | 
|  | 878 | /*============================================================================ | 
|  | 879 | * Driver I/O control. | 
|  | 880 | * o verify arguments | 
|  | 881 | * o perform requested action | 
|  | 882 | * | 
|  | 883 | * This function is called when router handles one of the reserved user | 
|  | 884 | * IOCTLs.  Note that 'arg' stil points to user address space. | 
|  | 885 | */ | 
|  | 886 | static int ioctl(struct wan_device* wandev, unsigned cmd, unsigned long arg) | 
|  | 887 | { | 
|  | 888 | sdla_t* card; | 
|  | 889 | int err; | 
|  | 890 |  | 
|  | 891 | /* sanity checks */ | 
|  | 892 | if ((wandev == NULL) || (wandev->private == NULL)) | 
|  | 893 | return -EFAULT; | 
|  | 894 | if (wandev->state == WAN_UNCONFIGURED) | 
|  | 895 | return -ENODEV; | 
|  | 896 |  | 
|  | 897 | card = wandev->private; | 
|  | 898 |  | 
|  | 899 | if(card->hw.type != SDLA_S514){ | 
|  | 900 | disable_irq(card->hw.irq); | 
|  | 901 | } | 
|  | 902 |  | 
|  | 903 | if (test_bit(SEND_CRIT, (void*)&wandev->critical)) { | 
|  | 904 | return -EAGAIN; | 
|  | 905 | } | 
|  | 906 |  | 
|  | 907 | switch (cmd) { | 
|  | 908 | case WANPIPE_DUMP: | 
|  | 909 | err = ioctl_dump(wandev->private, (void*)arg); | 
|  | 910 | break; | 
|  | 911 |  | 
|  | 912 | case WANPIPE_EXEC: | 
|  | 913 | err = ioctl_exec(wandev->private, (void*)arg, cmd); | 
|  | 914 | break; | 
|  | 915 | default: | 
|  | 916 | err = -EINVAL; | 
|  | 917 | } | 
|  | 918 |  | 
|  | 919 | return err; | 
|  | 920 | } | 
|  | 921 |  | 
|  | 922 | /****** Driver IOCTL Handlers ***********************************************/ | 
|  | 923 |  | 
|  | 924 | /*============================================================================ | 
|  | 925 | * Dump adapter memory to user buffer. | 
|  | 926 | * o verify request structure | 
|  | 927 | * o copy request structure to kernel data space | 
|  | 928 | * o verify length/offset | 
|  | 929 | * o verify user buffer | 
|  | 930 | * o copy adapter memory image to user buffer | 
|  | 931 | * | 
|  | 932 | * Note: when dumping memory, this routine switches curent dual-port memory | 
|  | 933 | *	 vector, so care must be taken to avoid racing conditions. | 
|  | 934 | */ | 
|  | 935 | static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump) | 
|  | 936 | { | 
|  | 937 | sdla_dump_t dump; | 
|  | 938 | unsigned winsize; | 
|  | 939 | unsigned long oldvec;	/* DPM window vector */ | 
|  | 940 | unsigned long smp_flags; | 
|  | 941 | int err = 0; | 
|  | 942 |  | 
|  | 943 | if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t))) | 
|  | 944 | return -EFAULT; | 
|  | 945 |  | 
|  | 946 | if ((dump.magic != WANPIPE_MAGIC) || | 
|  | 947 | (dump.offset + dump.length > card->hw.memory)) | 
|  | 948 | return -EINVAL; | 
|  | 949 |  | 
|  | 950 | winsize = card->hw.dpmsize; | 
|  | 951 |  | 
|  | 952 | if(card->hw.type != SDLA_S514) { | 
|  | 953 |  | 
|  | 954 | lock_adapter_irq(&card->wandev.lock, &smp_flags); | 
|  | 955 |  | 
|  | 956 | oldvec = card->hw.vector; | 
|  | 957 | while (dump.length) { | 
|  | 958 | /* current offset */ | 
|  | 959 | unsigned pos = dump.offset % winsize; | 
|  | 960 | /* current vector */ | 
|  | 961 | unsigned long vec = dump.offset - pos; | 
|  | 962 | unsigned len = (dump.length > (winsize - pos)) ? | 
|  | 963 | (winsize - pos) : dump.length; | 
|  | 964 | /* relocate window */ | 
|  | 965 | if (sdla_mapmem(&card->hw, vec) != 0) { | 
|  | 966 | err = -EIO; | 
|  | 967 | break; | 
|  | 968 | } | 
|  | 969 |  | 
|  | 970 | if(copy_to_user((void *)dump.ptr, | 
|  | 971 | (u8 *)card->hw.dpmbase + pos, len)){ | 
|  | 972 |  | 
|  | 973 | unlock_adapter_irq(&card->wandev.lock, &smp_flags); | 
|  | 974 | return -EFAULT; | 
|  | 975 | } | 
|  | 976 |  | 
|  | 977 | dump.length     -= len; | 
|  | 978 | dump.offset     += len; | 
|  | 979 | dump.ptr         = (char*)dump.ptr + len; | 
|  | 980 | } | 
|  | 981 |  | 
|  | 982 | sdla_mapmem(&card->hw, oldvec);/* restore DPM window position */ | 
|  | 983 | unlock_adapter_irq(&card->wandev.lock, &smp_flags); | 
|  | 984 |  | 
|  | 985 | }else { | 
|  | 986 |  | 
|  | 987 | if(copy_to_user((void *)dump.ptr, | 
|  | 988 | (u8 *)card->hw.dpmbase + dump.offset, dump.length)){ | 
|  | 989 | return -EFAULT; | 
|  | 990 | } | 
|  | 991 | } | 
|  | 992 |  | 
|  | 993 | return err; | 
|  | 994 | } | 
|  | 995 |  | 
|  | 996 | /*============================================================================ | 
|  | 997 | * Execute adapter firmware command. | 
|  | 998 | * o verify request structure | 
|  | 999 | * o copy request structure to kernel data space | 
|  | 1000 | * o call protocol-specific 'exec' function | 
|  | 1001 | */ | 
|  | 1002 | static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec, int cmd) | 
|  | 1003 | { | 
|  | 1004 | sdla_exec_t exec; | 
|  | 1005 | int err=0; | 
|  | 1006 |  | 
|  | 1007 | if (card->exec == NULL && cmd == WANPIPE_EXEC){ | 
|  | 1008 | return -ENODEV; | 
|  | 1009 | } | 
|  | 1010 |  | 
|  | 1011 | if(copy_from_user((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t))) | 
|  | 1012 | return -EFAULT; | 
|  | 1013 |  | 
|  | 1014 | if ((exec.magic != WANPIPE_MAGIC) || (exec.cmd == NULL)) | 
|  | 1015 | return -EINVAL; | 
|  | 1016 |  | 
|  | 1017 | switch (cmd) { | 
|  | 1018 | case WANPIPE_EXEC: | 
|  | 1019 | err = card->exec(card, exec.cmd, exec.data); | 
|  | 1020 | break; | 
|  | 1021 | } | 
|  | 1022 | return err; | 
|  | 1023 | } | 
|  | 1024 |  | 
|  | 1025 | /******* Miscellaneous ******************************************************/ | 
|  | 1026 |  | 
|  | 1027 | /*============================================================================ | 
|  | 1028 | * SDLA Interrupt Service Routine. | 
|  | 1029 | * o acknowledge SDLA hardware interrupt. | 
|  | 1030 | * o call protocol-specific interrupt service routine, if any. | 
|  | 1031 | */ | 
|  | 1032 | STATIC irqreturn_t sdla_isr (int irq, void* dev_id, struct pt_regs *regs) | 
|  | 1033 | { | 
|  | 1034 | #define	card	((sdla_t*)dev_id) | 
|  | 1035 |  | 
|  | 1036 | if(card->hw.type == SDLA_S514) {	/* handle interrrupt on S514 */ | 
|  | 1037 | u32 int_status; | 
|  | 1038 | unsigned char CPU_no = card->hw.S514_cpu_no[0]; | 
|  | 1039 | unsigned char card_found_for_IRQ; | 
|  | 1040 | u8 IRQ_count = 0; | 
|  | 1041 |  | 
|  | 1042 | for(;;) { | 
|  | 1043 |  | 
|  | 1044 | read_S514_int_stat(&card->hw, &int_status); | 
|  | 1045 |  | 
|  | 1046 | /* check if the interrupt is for this device */ | 
|  | 1047 | if(!((unsigned char)int_status & | 
|  | 1048 | (IRQ_CPU_A | IRQ_CPU_B))) | 
|  | 1049 | return IRQ_HANDLED; | 
|  | 1050 |  | 
|  | 1051 | /* if the IRQ is for both CPUs on the same adapter, */ | 
|  | 1052 | /* then alter the interrupt status so as to handle */ | 
|  | 1053 | /* one CPU at a time */ | 
|  | 1054 | if(((unsigned char)int_status & (IRQ_CPU_A | IRQ_CPU_B)) | 
|  | 1055 | == (IRQ_CPU_A | IRQ_CPU_B)) { | 
|  | 1056 | int_status &= (CPU_no == S514_CPU_A) ? | 
|  | 1057 | ~IRQ_CPU_B : ~IRQ_CPU_A; | 
|  | 1058 | } | 
|  | 1059 |  | 
|  | 1060 | card_found_for_IRQ = 0; | 
|  | 1061 |  | 
|  | 1062 | /* check to see that the CPU number for this device */ | 
|  | 1063 | /* corresponds to the interrupt status read */ | 
|  | 1064 | switch (CPU_no) { | 
|  | 1065 | case S514_CPU_A: | 
|  | 1066 | if((unsigned char)int_status & | 
|  | 1067 | IRQ_CPU_A) | 
|  | 1068 | card_found_for_IRQ = 1; | 
|  | 1069 | break; | 
|  | 1070 |  | 
|  | 1071 | case S514_CPU_B: | 
|  | 1072 | if((unsigned char)int_status & | 
|  | 1073 | IRQ_CPU_B) | 
|  | 1074 | card_found_for_IRQ = 1; | 
|  | 1075 | break; | 
|  | 1076 | } | 
|  | 1077 |  | 
|  | 1078 | /* exit if the interrupt is for another CPU on the */ | 
|  | 1079 | /* same IRQ */ | 
|  | 1080 | if(!card_found_for_IRQ) | 
|  | 1081 | return IRQ_HANDLED; | 
|  | 1082 |  | 
|  | 1083 | if (!card || | 
|  | 1084 | (card->wandev.state == WAN_UNCONFIGURED && !card->configured)){ | 
|  | 1085 | printk(KERN_INFO | 
|  | 1086 | "Received IRQ %d for CPU #%c\n", | 
|  | 1087 | irq, CPU_no); | 
|  | 1088 | printk(KERN_INFO | 
|  | 1089 | "IRQ for unconfigured adapter\n"); | 
|  | 1090 | S514_intack(&card->hw, int_status); | 
|  | 1091 | return IRQ_HANDLED; | 
|  | 1092 | } | 
|  | 1093 |  | 
|  | 1094 | if (card->in_isr) { | 
|  | 1095 | printk(KERN_INFO | 
|  | 1096 | "%s: interrupt re-entrancy on IRQ %d\n", | 
|  | 1097 | card->devname, card->wandev.irq); | 
|  | 1098 | S514_intack(&card->hw, int_status); | 
|  | 1099 | return IRQ_HANDLED; | 
|  | 1100 | } | 
|  | 1101 |  | 
|  | 1102 | spin_lock(&card->wandev.lock); | 
|  | 1103 | if (card->next){ | 
|  | 1104 | spin_lock(&card->next->wandev.lock); | 
|  | 1105 | } | 
|  | 1106 |  | 
|  | 1107 | S514_intack(&card->hw, int_status); | 
|  | 1108 | if (card->isr) | 
|  | 1109 | card->isr(card); | 
|  | 1110 |  | 
|  | 1111 | if (card->next){ | 
|  | 1112 | spin_unlock(&card->next->wandev.lock); | 
|  | 1113 | } | 
|  | 1114 | spin_unlock(&card->wandev.lock); | 
|  | 1115 |  | 
|  | 1116 | /* handle a maximum of two interrupts (one for each */ | 
|  | 1117 | /* CPU on the adapter) before returning */ | 
|  | 1118 | if((++ IRQ_count) == 2) | 
|  | 1119 | return IRQ_HANDLED; | 
|  | 1120 | } | 
|  | 1121 | } | 
|  | 1122 |  | 
|  | 1123 | else {			/* handle interrupt on S508 adapter */ | 
|  | 1124 |  | 
|  | 1125 | if (!card || ((card->wandev.state == WAN_UNCONFIGURED) && !card->configured)) | 
|  | 1126 | return IRQ_HANDLED; | 
|  | 1127 |  | 
|  | 1128 | if (card->in_isr) { | 
|  | 1129 | printk(KERN_INFO | 
|  | 1130 | "%s: interrupt re-entrancy on IRQ %d!\n", | 
|  | 1131 | card->devname, card->wandev.irq); | 
|  | 1132 | return IRQ_HANDLED; | 
|  | 1133 | } | 
|  | 1134 |  | 
|  | 1135 | spin_lock(&card->wandev.lock); | 
|  | 1136 | if (card->next){ | 
|  | 1137 | spin_lock(&card->next->wandev.lock); | 
|  | 1138 | } | 
|  | 1139 |  | 
|  | 1140 | sdla_intack(&card->hw); | 
|  | 1141 | if (card->isr) | 
|  | 1142 | card->isr(card); | 
|  | 1143 |  | 
|  | 1144 | if (card->next){ | 
|  | 1145 | spin_unlock(&card->next->wandev.lock); | 
|  | 1146 | } | 
|  | 1147 | spin_unlock(&card->wandev.lock); | 
|  | 1148 |  | 
|  | 1149 | } | 
|  | 1150 | return IRQ_HANDLED; | 
|  | 1151 | #undef	card | 
|  | 1152 | } | 
|  | 1153 |  | 
|  | 1154 | /*============================================================================ | 
|  | 1155 | * This routine is called by the protocol-specific modules when network | 
|  | 1156 | * interface is being open.  The only reason we need this, is because we | 
|  | 1157 | * have to call MOD_INC_USE_COUNT, but cannot include 'module.h' where it's | 
|  | 1158 | * defined more than once into the same kernel module. | 
|  | 1159 | */ | 
|  | 1160 | void wanpipe_open (sdla_t* card) | 
|  | 1161 | { | 
|  | 1162 | ++card->open_cnt; | 
|  | 1163 | } | 
|  | 1164 |  | 
|  | 1165 | /*============================================================================ | 
|  | 1166 | * This routine is called by the protocol-specific modules when network | 
|  | 1167 | * interface is being closed.  The only reason we need this, is because we | 
|  | 1168 | * have to call MOD_DEC_USE_COUNT, but cannot include 'module.h' where it's | 
|  | 1169 | * defined more than once into the same kernel module. | 
|  | 1170 | */ | 
|  | 1171 | void wanpipe_close (sdla_t* card) | 
|  | 1172 | { | 
|  | 1173 | --card->open_cnt; | 
|  | 1174 | } | 
|  | 1175 |  | 
|  | 1176 | /*============================================================================ | 
|  | 1177 | * Set WAN device state. | 
|  | 1178 | */ | 
|  | 1179 | void wanpipe_set_state (sdla_t* card, int state) | 
|  | 1180 | { | 
|  | 1181 | if (card->wandev.state != state) { | 
|  | 1182 | switch (state) { | 
|  | 1183 | case WAN_CONNECTED: | 
|  | 1184 | printk (KERN_INFO "%s: link connected!\n", | 
|  | 1185 | card->devname); | 
|  | 1186 | break; | 
|  | 1187 |  | 
|  | 1188 | case WAN_CONNECTING: | 
|  | 1189 | printk (KERN_INFO "%s: link connecting...\n", | 
|  | 1190 | card->devname); | 
|  | 1191 | break; | 
|  | 1192 |  | 
|  | 1193 | case WAN_DISCONNECTED: | 
|  | 1194 | printk (KERN_INFO "%s: link disconnected!\n", | 
|  | 1195 | card->devname); | 
|  | 1196 | break; | 
|  | 1197 | } | 
|  | 1198 | card->wandev.state = state; | 
|  | 1199 | } | 
|  | 1200 | card->state_tick = jiffies; | 
|  | 1201 | } | 
|  | 1202 |  | 
|  | 1203 | sdla_t * wanpipe_find_card (char *name) | 
|  | 1204 | { | 
|  | 1205 | int cnt; | 
|  | 1206 | for (cnt = 0; cnt < ncards; ++ cnt) { | 
|  | 1207 | sdla_t* card = &card_array[cnt]; | 
|  | 1208 | if (!strcmp(card->devname,name)) | 
|  | 1209 | return card; | 
|  | 1210 | } | 
|  | 1211 | return NULL; | 
|  | 1212 | } | 
|  | 1213 |  | 
|  | 1214 | sdla_t * wanpipe_find_card_num (int num) | 
|  | 1215 | { | 
|  | 1216 | if (num < 1 || num > ncards) | 
|  | 1217 | return NULL; | 
|  | 1218 | num--; | 
|  | 1219 | return &card_array[num]; | 
|  | 1220 | } | 
|  | 1221 |  | 
|  | 1222 | /* | 
|  | 1223 | * @work_pointer:	work_struct to be done; | 
|  | 1224 | * 			should already have PREPARE_WORK() or | 
|  | 1225 | * 			  INIT_WORK() done on it by caller; | 
|  | 1226 | */ | 
|  | 1227 | void wanpipe_queue_work (struct work_struct *work_pointer) | 
|  | 1228 | { | 
|  | 1229 | if (test_and_set_bit(1, (void*)&wanpipe_bh_critical)) | 
|  | 1230 | printk(KERN_INFO "CRITICAL IN QUEUING WORK\n"); | 
|  | 1231 |  | 
|  | 1232 | queue_work(wanpipe_wq, work_pointer); | 
|  | 1233 | clear_bit(1,(void*)&wanpipe_bh_critical); | 
|  | 1234 | } | 
|  | 1235 |  | 
|  | 1236 | void wakeup_sk_bh(struct net_device *dev) | 
|  | 1237 | { | 
|  | 1238 | wanpipe_common_t *chan = dev->priv; | 
|  | 1239 |  | 
|  | 1240 | if (test_bit(0,&chan->common_critical)) | 
|  | 1241 | return; | 
|  | 1242 |  | 
|  | 1243 | if (chan->sk && chan->tx_timer){ | 
|  | 1244 | chan->tx_timer->expires=jiffies+1; | 
|  | 1245 | add_timer(chan->tx_timer); | 
|  | 1246 | } | 
|  | 1247 | } | 
|  | 1248 |  | 
|  | 1249 | int change_dev_flags(struct net_device *dev, unsigned flags) | 
|  | 1250 | { | 
|  | 1251 | struct ifreq if_info; | 
|  | 1252 | mm_segment_t fs = get_fs(); | 
|  | 1253 | int err; | 
|  | 1254 |  | 
|  | 1255 | memset(&if_info, 0, sizeof(if_info)); | 
|  | 1256 | strcpy(if_info.ifr_name, dev->name); | 
|  | 1257 | if_info.ifr_flags = flags; | 
|  | 1258 |  | 
|  | 1259 | set_fs(get_ds());     /* get user space block */ | 
|  | 1260 | err = devinet_ioctl(SIOCSIFFLAGS, &if_info); | 
|  | 1261 | set_fs(fs); | 
|  | 1262 |  | 
|  | 1263 | return err; | 
|  | 1264 | } | 
|  | 1265 |  | 
|  | 1266 | unsigned long get_ip_address(struct net_device *dev, int option) | 
|  | 1267 | { | 
|  | 1268 |  | 
|  | 1269 | struct in_ifaddr *ifaddr; | 
|  | 1270 | struct in_device *in_dev; | 
|  | 1271 |  | 
|  | 1272 | if ((in_dev = __in_dev_get(dev)) == NULL){ | 
|  | 1273 | return 0; | 
|  | 1274 | } | 
|  | 1275 |  | 
|  | 1276 | if ((ifaddr = in_dev->ifa_list)== NULL ){ | 
|  | 1277 | return 0; | 
|  | 1278 | } | 
|  | 1279 |  | 
|  | 1280 | switch (option){ | 
|  | 1281 |  | 
|  | 1282 | case WAN_LOCAL_IP: | 
|  | 1283 | return ifaddr->ifa_local; | 
|  | 1284 | break; | 
|  | 1285 |  | 
|  | 1286 | case WAN_POINTOPOINT_IP: | 
|  | 1287 | return ifaddr->ifa_address; | 
|  | 1288 | break; | 
|  | 1289 |  | 
|  | 1290 | case WAN_NETMASK_IP: | 
|  | 1291 | return ifaddr->ifa_mask; | 
|  | 1292 | break; | 
|  | 1293 |  | 
|  | 1294 | case WAN_BROADCAST_IP: | 
|  | 1295 | return ifaddr->ifa_broadcast; | 
|  | 1296 | break; | 
|  | 1297 | default: | 
|  | 1298 | return 0; | 
|  | 1299 | } | 
|  | 1300 |  | 
|  | 1301 | return 0; | 
|  | 1302 | } | 
|  | 1303 |  | 
|  | 1304 | void add_gateway(sdla_t *card, struct net_device *dev) | 
|  | 1305 | { | 
|  | 1306 | mm_segment_t oldfs; | 
|  | 1307 | struct rtentry route; | 
|  | 1308 | int res; | 
|  | 1309 |  | 
|  | 1310 | memset((char*)&route,0,sizeof(struct rtentry)); | 
|  | 1311 |  | 
|  | 1312 | ((struct sockaddr_in *) | 
|  | 1313 | &(route.rt_dst))->sin_addr.s_addr = 0; | 
|  | 1314 | ((struct sockaddr_in *) | 
|  | 1315 | &(route.rt_dst))->sin_family = AF_INET; | 
|  | 1316 |  | 
|  | 1317 | ((struct sockaddr_in *) | 
|  | 1318 | &(route.rt_genmask))->sin_addr.s_addr = 0; | 
|  | 1319 | ((struct sockaddr_in *) | 
|  | 1320 | &(route.rt_genmask)) ->sin_family = AF_INET; | 
|  | 1321 |  | 
|  | 1322 |  | 
|  | 1323 | route.rt_flags = 0; | 
|  | 1324 | route.rt_dev = dev->name; | 
|  | 1325 |  | 
|  | 1326 | oldfs = get_fs(); | 
|  | 1327 | set_fs(get_ds()); | 
|  | 1328 | res = ip_rt_ioctl(SIOCADDRT,&route); | 
|  | 1329 | set_fs(oldfs); | 
|  | 1330 |  | 
|  | 1331 | if (res == 0){ | 
|  | 1332 | printk(KERN_INFO "%s: Gateway added for %s\n", | 
|  | 1333 | card->devname,dev->name); | 
|  | 1334 | } | 
|  | 1335 |  | 
|  | 1336 | return; | 
|  | 1337 | } | 
|  | 1338 |  | 
|  | 1339 | MODULE_LICENSE("GPL"); | 
|  | 1340 |  | 
|  | 1341 | /****** End *********************************************************/ |