main.c 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
/*
 * IPWireless 3G PCMCIA Network Driver
 *
 * Original code
 *   by Stephen Blackheath <stephen@blacksapphire.com>,
 *      Ben Martel <benm@symmetric.co.nz>
 *
 * Copyrighted as follows:
 *   Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
 *
 * Various driver changes and rewrites, port to new kernels
 *   Copyright (C) 2006-2007 Jiri Kosina
 *
 * Misc code cleanups and updates
 *   Copyright (C) 2007 David Sterba
 */

#include "hardware.h"
#include "network.h"
#include "main.h"
#include "tty.h"

#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>

#include <pcmcia/cisreg.h>
#include <pcmcia/device_id.h>
#include <pcmcia/ss.h>
#include <pcmcia/ds.h>
#include <pcmcia/cs.h>

static struct pcmcia_device_id ipw_ids[] = {
	PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0100),
	PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0200),
	PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, ipw_ids);

static void ipwireless_detach(struct pcmcia_device *link);

/*
 * Module params
 */
/* Debug mode: more verbose, print sent/recv bytes */
int ipwireless_debug;
int ipwireless_loopback;
52
int ipwireless_out_queue = 10;
53 54 55 56 57 58 59

module_param_named(debug, ipwireless_debug, int, 0);
module_param_named(loopback, ipwireless_loopback, int, 0);
module_param_named(out_queue, ipwireless_out_queue, int, 0);
MODULE_PARM_DESC(debug, "switch on debug messages [0]");
MODULE_PARM_DESC(loopback,
		"debug: enable ras_raw channel [0]");
60
MODULE_PARM_DESC(out_queue, "debug: set size of outgoing PPP queue [10]");
61 62 63 64 65 66 67

/* Executes in process context. */
static void signalled_reboot_work(struct work_struct *work_reboot)
{
	struct ipw_dev *ipw = container_of(work_reboot, struct ipw_dev,
			work_reboot);
	struct pcmcia_device *link = ipw->link;
68
	pcmcia_reset_card(link->socket);
69 70 71 72 73 74 75 76 77 78
}

static void signalled_reboot_callback(void *callback_data)
{
	struct ipw_dev *ipw = (struct ipw_dev *) callback_data;

	/* Delegate to process context. */
	schedule_work(&ipw->work_reboot);
}

79 80 81 82 83
static int ipwireless_probe(struct pcmcia_device *p_dev,
			    cistpl_cftable_entry_t *cfg,
			    cistpl_cftable_entry_t *dflt,
			    unsigned int vcc,
			    void *priv_data)
84
{
85 86 87 88 89 90
	struct ipw_dev *ipw = priv_data;
	struct resource *io_resource;
	memreq_t memreq_attr_memory;
	memreq_t memreq_common_memory;
	int ret;

91 92 93 94 95 96 97 98
	p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
	p_dev->io.BasePort1 = cfg->io.win[0].base;
	p_dev->io.NumPorts1 = cfg->io.win[0].len;
	p_dev->io.IOAddrLines = 16;

	/* 0x40 causes it to generate level mode interrupts. */
	/* 0x04 enables IREQ pin. */
	p_dev->conf.ConfigIndex = cfg->index | 0x44;
99 100 101
	ret = pcmcia_request_io(p_dev, &p_dev->io);
	if (ret)
		return ret;
102

103 104
	io_resource = request_region(p_dev->io.BasePort1, p_dev->io.NumPorts1,
				IPWIRELESS_PCCARD_NAME);
105

106 107
	if (cfg->mem.nwin == 0)
		return 0;
108

109 110 111 112 113 114 115 116
	ipw->request_common_memory.Attributes =
		WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE;
	ipw->request_common_memory.Base = cfg->mem.win[0].host_addr;
	ipw->request_common_memory.Size = cfg->mem.win[0].len;
	if (ipw->request_common_memory.Size < 0x1000)
		ipw->request_common_memory.Size = 0x1000;
	ipw->request_common_memory.AccessSpeed = 0;

117
	ret = pcmcia_request_window(p_dev, &ipw->request_common_memory,
118
				&ipw->handle_common_memory);
119

120
	if (ret != 0)
121
		goto exit1;
122

123 124
	memreq_common_memory.CardOffset = cfg->mem.win[0].card_addr;
	memreq_common_memory.Page = 0;
125

126
	ret = pcmcia_map_mem_page(p_dev, ipw->handle_common_memory,
127
				&memreq_common_memory);
128

129
	if (ret != 0)
130
		goto exit2;
131

132
	ipw->is_v2_card = cfg->mem.win[0].len == 0x100;
133

134 135 136 137 138 139 140 141 142 143 144
	ipw->common_memory = ioremap(ipw->request_common_memory.Base,
				ipw->request_common_memory.Size);
	request_mem_region(ipw->request_common_memory.Base,
			ipw->request_common_memory.Size,
			IPWIRELESS_PCCARD_NAME);

	ipw->request_attr_memory.Attributes =
		WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | WIN_ENABLE;
	ipw->request_attr_memory.Base = 0;
	ipw->request_attr_memory.Size = 0;	/* this used to be 0x1000 */
	ipw->request_attr_memory.AccessSpeed = 0;
145

146
	ret = pcmcia_request_window(p_dev, &ipw->request_attr_memory,
147
				&ipw->handle_attr_memory);
148

149
	if (ret != 0)
150
		goto exit2;
151

152 153
	memreq_attr_memory.CardOffset = 0;
	memreq_attr_memory.Page = 0;
154

155
	ret = pcmcia_map_mem_page(p_dev, ipw->handle_attr_memory,
156
				&memreq_attr_memory);
157

158
	if (ret != 0)
159
		goto exit3;
160

161 162 163 164
	ipw->attr_memory = ioremap(ipw->request_attr_memory.Base,
				ipw->request_attr_memory.Size);
	request_mem_region(ipw->request_attr_memory.Base,
			ipw->request_attr_memory.Size, IPWIRELESS_PCCARD_NAME);
165

166
	return 0;
167

168
exit3:
169
	pcmcia_release_window(p_dev, ipw->handle_attr_memory);
170 171 172
exit2:
	if (ipw->common_memory) {
		release_mem_region(ipw->request_common_memory.Base,
173
				ipw->request_common_memory.Size);
174
		iounmap(ipw->common_memory);
175
		pcmcia_release_window(p_dev, ipw->handle_common_memory);
176
	} else
177
		pcmcia_release_window(p_dev, ipw->handle_common_memory);
178 179 180 181 182
exit1:
	release_resource(io_resource);
	pcmcia_disable_device(p_dev);
	return -1;
}
183

184 185 186 187
static int config_ipwireless(struct ipw_dev *ipw)
{
	struct pcmcia_device *link = ipw->link;
	int ret = 0;
188

189
	ipw->is_v2_card = 0;
190

191
	ret = pcmcia_loop_config(link, ipwireless_probe, ipw);
192
	if (ret != 0)
193
		return ret;
194

195 196
	link->conf.Attributes = CONF_ENABLE_IRQ;
	link->conf.IntType = INT_MEMORY_AND_IO;
197

198
	link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
199
	link->irq.Handler = ipwireless_interrupt;
200 201 202 203 204 205 206 207 208 209

	INIT_WORK(&ipw->work_reboot, signalled_reboot_work);

	ipwireless_init_hardware_v1(ipw->hardware, link->io.BasePort1,
				    ipw->attr_memory, ipw->common_memory,
				    ipw->is_v2_card, signalled_reboot_callback,
				    ipw);

	ret = pcmcia_request_irq(link, &link->irq);

210
	if (ret != 0)
211
		goto exit;
212 213 214 215 216 217 218 219 220 221 222

	printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": Card type %s\n",
			ipw->is_v2_card ? "V2/V3" : "V1");
	printk(KERN_INFO IPWIRELESS_PCCARD_NAME
			": I/O ports 0x%04x-0x%04x, irq %d\n",
			(unsigned int) link->io.BasePort1,
			(unsigned int) (link->io.BasePort1 +
				link->io.NumPorts1 - 1),
			(unsigned int) link->irq.AssignedIRQ);
	if (ipw->attr_memory && ipw->common_memory)
		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
223
			": attr memory 0x%08lx-0x%08lx, common memory 0x%08lx-0x%08lx\n",
224 225 226 227 228 229
			ipw->request_attr_memory.Base,
			ipw->request_attr_memory.Base
			+ ipw->request_attr_memory.Size - 1,
			ipw->request_common_memory.Base,
			ipw->request_common_memory.Base
			+ ipw->request_common_memory.Size - 1);
230 231 232

	ipw->network = ipwireless_network_create(ipw->hardware);
	if (!ipw->network)
233
		goto exit;
234 235 236 237

	ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network,
			ipw->nodes);
	if (!ipw->tty)
238
		goto exit;
239 240 241 242 243 244 245 246 247

	ipwireless_init_hardware_v2_v3(ipw->hardware);

	/*
	 * Do the RequestConfiguration last, because it enables interrupts.
	 * Then we don't get any interrupts before we're ready for them.
	 */
	ret = pcmcia_request_configuration(link, &link->conf);

248
	if (ret != 0)
249
		goto exit;
250 251 252 253 254

	link->dev_node = &ipw->nodes[0];

	return 0;

255
exit:
256
	if (ipw->attr_memory) {
257 258
		release_mem_region(ipw->request_attr_memory.Base,
				ipw->request_attr_memory.Size);
259
		iounmap(ipw->attr_memory);
260
		pcmcia_release_window(link, ipw->handle_attr_memory);
261 262
	}
	if (ipw->common_memory) {
263 264
		release_mem_region(ipw->request_common_memory.Base,
				ipw->request_common_memory.Size);
265
		iounmap(ipw->common_memory);
266
		pcmcia_release_window(link, ipw->handle_common_memory);
267 268 269 270 271 272 273
	}
	pcmcia_disable_device(link);
	return -1;
}

static void release_ipwireless(struct ipw_dev *ipw)
{
274
	pcmcia_disable_device(ipw->link);
275

276 277 278
	if (ipw->common_memory) {
		release_mem_region(ipw->request_common_memory.Base,
				ipw->request_common_memory.Size);
279
		iounmap(ipw->common_memory);
280 281 282 283
	}
	if (ipw->attr_memory) {
		release_mem_region(ipw->request_attr_memory.Base,
				ipw->request_attr_memory.Size);
284
		iounmap(ipw->attr_memory);
285
	}
286
	if (ipw->common_memory)
287
		pcmcia_release_window(ipw->link, ipw->handle_common_memory);
288
	if (ipw->attr_memory)
289
		pcmcia_release_window(ipw->link, ipw->handle_attr_memory);
290 291 292

	/* Break the link with Card Services */
	pcmcia_disable_device(ipw->link);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
}

/*
 * ipwireless_attach() creates an "instance" of the driver, allocating
 * local data structures for one device (one interface).  The device
 * is registered with Card Services.
 *
 * The pcmcia_device structure is initialized, but we don't actually
 * configure the card at this point -- we wait until we receive a
 * card insertion event.
 */
static int ipwireless_attach(struct pcmcia_device *link)
{
	struct ipw_dev *ipw;
	int ret;

	ipw = kzalloc(sizeof(struct ipw_dev), GFP_KERNEL);
	if (!ipw)
		return -ENOMEM;

	ipw->link = link;
	link->priv = ipw;

	/* Link this device into our device list. */
	link->dev_node = &ipw->nodes[0];

	ipw->hardware = ipwireless_hardware_create();
	if (!ipw->hardware) {
		kfree(ipw);
		return -ENOMEM;
	}
	/* RegisterClient will call config_ipwireless */

	ret = config_ipwireless(ipw);

	if (ret != 0) {
		ipwireless_detach(link);
		return ret;
	}

	return 0;
}

/*
 * This deletes a driver "instance".  The device is de-registered with
 * Card Services.  If it has been released, all local data structures
 * are freed.  Otherwise, the structures will be freed when the device
 * is released.
 */
static void ipwireless_detach(struct pcmcia_device *link)
{
	struct ipw_dev *ipw = link->priv;

	release_ipwireless(ipw);

	if (ipw->tty != NULL)
		ipwireless_tty_free(ipw->tty);
	if (ipw->network != NULL)
		ipwireless_network_free(ipw->network);
	if (ipw->hardware != NULL)
		ipwireless_hardware_free(ipw->hardware);
	kfree(ipw);
}

static struct pcmcia_driver me = {
	.owner		= THIS_MODULE,
	.probe          = ipwireless_attach,
	.remove         = ipwireless_detach,
	.drv = { .name  = IPWIRELESS_PCCARD_NAME },
	.id_table       = ipw_ids
};

/*
 * Module insertion : initialisation of the module.
 * Register the card with cardmgr...
 */
static int __init init_ipwireless(void)
{
	int ret;

	printk(KERN_INFO IPWIRELESS_PCCARD_NAME " "
	       IPWIRELESS_PCMCIA_VERSION " by " IPWIRELESS_PCMCIA_AUTHOR "\n");

	ret = ipwireless_tty_init();
	if (ret != 0)
		return ret;

	ret = pcmcia_register_driver(&me);
	if (ret != 0)
		ipwireless_tty_release();

	return ret;
}

/*
 * Module removal
 */
static void __exit exit_ipwireless(void)
{
	printk(KERN_INFO IPWIRELESS_PCCARD_NAME " "
			IPWIRELESS_PCMCIA_VERSION " removed\n");

	pcmcia_unregister_driver(&me);
	ipwireless_tty_release();
}

module_init(init_ipwireless);
module_exit(exit_ipwireless);

MODULE_AUTHOR(IPWIRELESS_PCMCIA_AUTHOR);
MODULE_DESCRIPTION(IPWIRELESS_PCCARD_NAME " " IPWIRELESS_PCMCIA_VERSION);
MODULE_LICENSE("GPL");