cs.c 21.4 KB
Newer Older
L
Linus Torvalds 已提交
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
/*
 * cs.c -- Kernel Card Services - core services
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * The initial developer of the original code is David A. Hinds
 * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
 * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 *
 * (C) 1999		David A. Hinds
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/device.h>
30
#include <linux/kthread.h>
31
#include <linux/freezer.h>
L
Linus Torvalds 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#include <asm/system.h>
#include <asm/irq.h>

#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include "cs_internal.h"


/* Module parameters */

MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
D
Dominik Brodowski 已提交
47
MODULE_DESCRIPTION("Linux Kernel Card Services");
L
Linus Torvalds 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
MODULE_LICENSE("GPL");

#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)

INT_MODULE_PARM(setup_delay,	10);		/* centiseconds */
INT_MODULE_PARM(resume_delay,	20);		/* centiseconds */
INT_MODULE_PARM(shutdown_delay,	3);		/* centiseconds */
INT_MODULE_PARM(vcc_settle,	40);		/* centiseconds */
INT_MODULE_PARM(reset_time,	10);		/* usecs */
INT_MODULE_PARM(unreset_delay,	10);		/* centiseconds */
INT_MODULE_PARM(unreset_check,	10);		/* centiseconds */
INT_MODULE_PARM(unreset_limit,	30);		/* unreset_check's */

/* Access speed for attribute memory windows */
INT_MODULE_PARM(cis_speed,	300);		/* ns */


socket_state_t dead_socket = {
	.csc_mask	= SS_DETECT,
};
D
Dominik Brodowski 已提交
68
EXPORT_SYMBOL(dead_socket);
L
Linus Torvalds 已提交
69 70 71 72 73 74


/* List of all sockets, protected by a rwsem */
LIST_HEAD(pcmcia_socket_list);
EXPORT_SYMBOL(pcmcia_socket_list);

D
Dominik Brodowski 已提交
75 76
DECLARE_RWSEM(pcmcia_socket_list_rwsem);
EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
L
Linus Torvalds 已提交
77 78


R
Randy Dunlap 已提交
79
/*
D
Dominik Brodowski 已提交
80 81 82 83
 * Low-level PCMCIA socket drivers need to register with the PCCard
 * core using pcmcia_register_socket.
 *
 * socket drivers are expected to use the following callbacks in their
L
Linus Torvalds 已提交
84 85 86 87 88 89 90 91 92
 * .drv struct:
 *  - pcmcia_socket_dev_suspend
 *  - pcmcia_socket_dev_resume
 * These functions check for the appropriate struct pcmcia_soket arrays,
 * and pass them to the low-level functions pcmcia_{suspend,resume}_socket
 */
static int socket_resume(struct pcmcia_socket *skt);
static int socket_suspend(struct pcmcia_socket *skt);

93
int pcmcia_socket_dev_suspend(struct device *dev)
L
Linus Torvalds 已提交
94 95 96 97 98
{
	struct pcmcia_socket *socket;

	down_read(&pcmcia_socket_list_rwsem);
	list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
99
		if (socket->dev.parent != dev)
L
Linus Torvalds 已提交
100
			continue;
101
		mutex_lock(&socket->skt_mutex);
L
Linus Torvalds 已提交
102
		socket_suspend(socket);
103
		mutex_unlock(&socket->skt_mutex);
L
Linus Torvalds 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116
	}
	up_read(&pcmcia_socket_list_rwsem);

	return 0;
}
EXPORT_SYMBOL(pcmcia_socket_dev_suspend);

int pcmcia_socket_dev_resume(struct device *dev)
{
	struct pcmcia_socket *socket;

	down_read(&pcmcia_socket_list_rwsem);
	list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
117
		if (socket->dev.parent != dev)
L
Linus Torvalds 已提交
118
			continue;
119
		mutex_lock(&socket->skt_mutex);
L
Linus Torvalds 已提交
120
		socket_resume(socket);
121
		mutex_unlock(&socket->skt_mutex);
L
Linus Torvalds 已提交
122 123 124 125 126 127 128 129 130 131
	}
	up_read(&pcmcia_socket_list_rwsem);

	return 0;
}
EXPORT_SYMBOL(pcmcia_socket_dev_resume);


struct pcmcia_socket * pcmcia_get_socket(struct pcmcia_socket *skt)
{
132 133
	struct device *dev = get_device(&skt->dev);
	if (!dev)
L
Linus Torvalds 已提交
134
		return NULL;
135
	skt = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
136
	if (!try_module_get(skt->owner)) {
137
		put_device(&skt->dev);
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145 146 147
		return NULL;
	}
	return (skt);
}
EXPORT_SYMBOL(pcmcia_get_socket);


void pcmcia_put_socket(struct pcmcia_socket *skt)
{
	module_put(skt->owner);
148
	put_device(&skt->dev);
L
Linus Torvalds 已提交
149 150 151 152
}
EXPORT_SYMBOL(pcmcia_put_socket);


153
static void pcmcia_release_socket(struct device *dev)
L
Linus Torvalds 已提交
154
{
155
	struct pcmcia_socket *socket = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
156 157 158 159 160 161 162 163

	complete(&socket->socket_released);
}

static int pccardd(void *__skt);

/**
 * pcmcia_register_socket - add a new pcmcia socket device
R
Randy Dunlap 已提交
164
 * @socket: the &socket to register
L
Linus Torvalds 已提交
165 166 167
 */
int pcmcia_register_socket(struct pcmcia_socket *socket)
{
168
	struct task_struct *tsk;
L
Linus Torvalds 已提交
169 170
	int ret;

171
	if (!socket || !socket->ops || !socket->dev.parent || !socket->resource_ops)
L
Linus Torvalds 已提交
172 173
		return -EINVAL;

174
	dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops);
L
Linus Torvalds 已提交
175 176 177 178

	spin_lock_init(&socket->lock);

	/* try to obtain a socket number [yes, it gets ugly if we
D
Dominik Brodowski 已提交
179 180
	 * register more than 2^sizeof(unsigned int) pcmcia
	 * sockets... but the socket number is deprecated
L
Linus Torvalds 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
	 * anyways, so I don't care] */
	down_write(&pcmcia_socket_list_rwsem);
	if (list_empty(&pcmcia_socket_list))
		socket->sock = 0;
	else {
		unsigned int found, i = 1;
		struct pcmcia_socket *tmp;
		do {
			found = 1;
			list_for_each_entry(tmp, &pcmcia_socket_list, socket_list) {
				if (tmp->sock == i)
					found = 0;
			}
			i++;
		} while (!found);
		socket->sock = i - 1;
	}
	list_add_tail(&socket->socket_list, &pcmcia_socket_list);
	up_write(&pcmcia_socket_list_rwsem);

201 202 203 204 205 206 207
#ifndef CONFIG_CARDBUS
	/*
	 * If we do not support Cardbus, ensure that
	 * the Cardbus socket capability is disabled.
	 */
	socket->features &= ~SS_CAP_CARDBUS;
#endif
L
Linus Torvalds 已提交
208 209

	/* set proper values in socket->dev */
210
	dev_set_drvdata(&socket->dev, socket);
L
Linus Torvalds 已提交
211
	socket->dev.class = &pcmcia_socket_class;
212
	dev_set_name(&socket->dev, "pcmcia_socket%u", socket->sock);
L
Linus Torvalds 已提交
213 214 215 216 217 218 219 220 221

	/* base address = 0, map = 0 */
	socket->cis_mem.flags = 0;
	socket->cis_mem.speed = cis_speed;

	INIT_LIST_HEAD(&socket->cis_cache);

	init_completion(&socket->socket_released);
	init_completion(&socket->thread_done);
222
	mutex_init(&socket->skt_mutex);
L
Linus Torvalds 已提交
223 224
	spin_lock_init(&socket->thread_lock);

225 226 227 228 229 230
	if (socket->resource_ops->init) {
		ret = socket->resource_ops->init(socket);
		if (ret)
			goto err;
	}

231 232 233
	tsk = kthread_run(pccardd, socket, "pccardd");
	if (IS_ERR(tsk)) {
		ret = PTR_ERR(tsk);
L
Linus Torvalds 已提交
234
		goto err;
235
	}
L
Linus Torvalds 已提交
236 237

	wait_for_completion(&socket->thread_done);
238
	if (!socket->thread) {
239 240
		dev_printk(KERN_WARNING, &socket->dev,
			   "PCMCIA: warning: socket thread did not start\n");
L
Linus Torvalds 已提交
241 242
		return -EIO;
	}
243

L
Linus Torvalds 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	pcmcia_parse_events(socket, SS_DETECT);

	return 0;

 err:
	down_write(&pcmcia_socket_list_rwsem);
	list_del(&socket->socket_list);
	up_write(&pcmcia_socket_list_rwsem);
	return ret;
} /* pcmcia_register_socket */
EXPORT_SYMBOL(pcmcia_register_socket);


/**
 * pcmcia_unregister_socket - remove a pcmcia socket device
R
Randy Dunlap 已提交
259
 * @socket: the &socket to unregister
L
Linus Torvalds 已提交
260 261 262 263 264 265
 */
void pcmcia_unregister_socket(struct pcmcia_socket *socket)
{
	if (!socket)
		return;

266
	dev_dbg(&socket->dev, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
L
Linus Torvalds 已提交
267

C
Christoph Hellwig 已提交
268
	if (socket->thread)
269
		kthread_stop(socket->thread);
C
Christoph Hellwig 已提交
270

L
Linus Torvalds 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
	release_cis_mem(socket);

	/* remove from our own list */
	down_write(&pcmcia_socket_list_rwsem);
	list_del(&socket->socket_list);
	up_write(&pcmcia_socket_list_rwsem);

	/* wait for sysfs to drop all references */
	release_resource_db(socket);
	wait_for_completion(&socket->socket_released);
} /* pcmcia_unregister_socket */
EXPORT_SYMBOL(pcmcia_unregister_socket);


struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr)
{
	struct pcmcia_socket *s;

	down_read(&pcmcia_socket_list_rwsem);
	list_for_each_entry(s, &pcmcia_socket_list, socket_list)
		if (s->sock == nr) {
			up_read(&pcmcia_socket_list_rwsem);
			return s;
		}
	up_read(&pcmcia_socket_list_rwsem);

	return NULL;

}
EXPORT_SYMBOL(pcmcia_get_socket_by_nr);

R
Randy Dunlap 已提交
302
/*
D
Dominik Brodowski 已提交
303 304 305 306 307 308
 * The central event handler.  Send_event() sends an event to the
 * 16-bit subsystem, which then calls the relevant device drivers.
 * Parse_events() interprets the event bits from
 * a card status change report.  Do_shutdown() handles the high
 * priority stuff associated with a card removal.
 */
L
Linus Torvalds 已提交
309 310 311 312 313 314 315 316 317 318

/* NOTE: send_event needs to be called with skt->sem held. */

static int send_event(struct pcmcia_socket *s, event_t event, int priority)
{
	int ret;

	if (s->state & SOCKET_CARDBUS)
		return 0;

319
	dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
L
Linus Torvalds 已提交
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
	   event, priority, s->callback);

	if (!s->callback)
		return 0;
	if (!try_module_get(s->callback->owner))
		return 0;

	ret = s->callback->event(s, event, priority);

	module_put(s->callback->owner);

	return ret;
}

static void socket_remove_drivers(struct pcmcia_socket *skt)
{
336
	dev_dbg(&skt->dev, "remove_drivers\n");
L
Linus Torvalds 已提交
337 338 339 340 341 342 343 344

	send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
}

static int socket_reset(struct pcmcia_socket *skt)
{
	int status, i;

345
	dev_dbg(&skt->dev, "reset\n");
L
Linus Torvalds 已提交
346 347 348 349 350 351 352 353 354 355 356 357 358

	skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
	skt->ops->set_socket(skt, &skt->socket);
	udelay((long)reset_time);

	skt->socket.flags &= ~SS_RESET;
	skt->ops->set_socket(skt, &skt->socket);

	msleep(unreset_delay * 10);
	for (i = 0; i < unreset_limit; i++) {
		skt->ops->get_status(skt, &status);

		if (!(status & SS_DETECT))
D
Dominik Brodowski 已提交
359
			return -ENODEV;
L
Linus Torvalds 已提交
360 361

		if (status & SS_READY)
D
Dominik Brodowski 已提交
362
			return 0;
L
Linus Torvalds 已提交
363 364 365 366

		msleep(unreset_check * 10);
	}

367
	dev_printk(KERN_ERR, &skt->dev, "time out after reset.\n");
368
	return -ETIMEDOUT;
L
Linus Torvalds 已提交
369 370
}

R
Randy Dunlap 已提交
371
/*
372 373 374 375 376 377 378 379 380
 * socket_setup() and socket_shutdown() are called by the main event handler
 * when card insertion and removal events are received.
 * socket_setup() turns on socket power and resets the socket, in two stages.
 * socket_shutdown() unconfigures a socket and turns off socket power.
 */
static void socket_shutdown(struct pcmcia_socket *s)
{
	int status;

381
	dev_dbg(&s->dev, "shutdown\n");
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399

	socket_remove_drivers(s);
	s->state &= SOCKET_INUSE | SOCKET_PRESENT;
	msleep(shutdown_delay * 10);
	s->state &= SOCKET_INUSE;

	/* Blank out the socket state */
	s->socket = dead_socket;
	s->ops->init(s);
	s->ops->set_socket(s, &s->socket);
	s->irq.AssignedIRQ = s->irq.Config = 0;
	s->lock_count = 0;
	destroy_cis_cache(s);
#ifdef CONFIG_CARDBUS
	cb_free(s);
#endif
	s->functions = 0;

400 401 402
	/* give socket some time to power down */
	msleep(100);

403 404
	s->ops->get_status(s, &status);
	if (status & SS_POWERON) {
405 406
		dev_printk(KERN_ERR, &s->dev,
			   "*** DANGER *** unable to remove socket power\n");
407 408 409 410 411
	}

	cs_socket_put(s);
}

L
Linus Torvalds 已提交
412 413 414 415
static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
{
	int status, i;

416
	dev_dbg(&skt->dev, "setup\n");
L
Linus Torvalds 已提交
417 418 419

	skt->ops->get_status(skt, &status);
	if (!(status & SS_DETECT))
D
Dominik Brodowski 已提交
420
		return -ENODEV;
L
Linus Torvalds 已提交
421 422 423 424 425 426

	msleep(initial_delay * 10);

	for (i = 0; i < 100; i++) {
		skt->ops->get_status(skt, &status);
		if (!(status & SS_DETECT))
D
Dominik Brodowski 已提交
427
			return -ENODEV;
L
Linus Torvalds 已提交
428 429 430 431 432 433 434 435

		if (!(status & SS_PENDING))
			break;

		msleep(100);
	}

	if (status & SS_PENDING) {
436 437
		dev_printk(KERN_ERR, &skt->dev,
			   "voltage interrogation timed out.\n");
438
		return -ETIMEDOUT;
L
Linus Torvalds 已提交
439 440 441
	}

	if (status & SS_CARDBUS) {
442
		if (!(skt->features & SS_CAP_CARDBUS)) {
443 444
			dev_printk(KERN_ERR, &skt->dev,
				"cardbus cards are not supported.\n");
445
			return -EINVAL;
446
		}
L
Linus Torvalds 已提交
447 448 449 450 451 452 453 454 455 456 457
		skt->state |= SOCKET_CARDBUS;
	}

	/*
	 * Decode the card voltage requirements, and apply power to the card.
	 */
	if (status & SS_3VCARD)
		skt->socket.Vcc = skt->socket.Vpp = 33;
	else if (!(status & SS_XVCARD))
		skt->socket.Vcc = skt->socket.Vpp = 50;
	else {
458
		dev_printk(KERN_ERR, &skt->dev, "unsupported voltage key.\n");
459
		return -EIO;
L
Linus Torvalds 已提交
460
	}
461 462 463 464

	if (skt->power_hook)
		skt->power_hook(skt, HOOK_POWER_PRE);

L
Linus Torvalds 已提交
465 466 467 468 469 470 471 472 473 474
	skt->socket.flags = 0;
	skt->ops->set_socket(skt, &skt->socket);

	/*
	 * Wait "vcc_settle" for the supply to stabilise.
	 */
	msleep(vcc_settle * 10);

	skt->ops->get_status(skt, &status);
	if (!(status & SS_POWERON)) {
475
		dev_printk(KERN_ERR, &skt->dev, "unable to apply power.\n");
476
		return -EIO;
L
Linus Torvalds 已提交
477 478
	}

479 480 481 482 483 484
	status = socket_reset(skt);

	if (skt->power_hook)
		skt->power_hook(skt, HOOK_POWER_POST);

	return status;
L
Linus Torvalds 已提交
485 486 487 488 489 490 491 492 493 494
}

/*
 * Handle card insertion.  Setup the socket, reset the card,
 * and then tell the rest of PCMCIA that a card is present.
 */
static int socket_insert(struct pcmcia_socket *skt)
{
	int ret;

495
	dev_dbg(&skt->dev, "insert\n");
L
Linus Torvalds 已提交
496 497

	if (!cs_socket_get(skt))
D
Dominik Brodowski 已提交
498
		return -ENODEV;
L
Linus Torvalds 已提交
499 500

	ret = socket_setup(skt, setup_delay);
D
Dominik Brodowski 已提交
501
	if (ret == 0) {
L
Linus Torvalds 已提交
502
		skt->state |= SOCKET_PRESENT;
503

504 505 506 507
		dev_printk(KERN_NOTICE, &skt->dev,
			   "pccard: %s card inserted into slot %d\n",
			   (skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA",
			   skt->sock);
508

L
Linus Torvalds 已提交
509 510 511 512 513 514
#ifdef CONFIG_CARDBUS
		if (skt->state & SOCKET_CARDBUS) {
			cb_alloc(skt);
			skt->state |= SOCKET_CARDBUS_CONFIG;
		}
#endif
515
		dev_dbg(&skt->dev, "insert done\n");
L
Linus Torvalds 已提交
516 517 518 519 520 521 522 523 524 525 526 527

		send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
	} else {
		socket_shutdown(skt);
	}

	return ret;
}

static int socket_suspend(struct pcmcia_socket *skt)
{
	if (skt->state & SOCKET_SUSPEND)
D
Dominik Brodowski 已提交
528
		return -EBUSY;
L
Linus Torvalds 已提交
529 530 531 532 533 534 535 536

	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
	skt->socket = dead_socket;
	skt->ops->set_socket(skt, &skt->socket);
	if (skt->ops->suspend)
		skt->ops->suspend(skt);
	skt->state |= SOCKET_SUSPEND;

D
Dominik Brodowski 已提交
537
	return 0;
L
Linus Torvalds 已提交
538 539 540 541 542 543 544 545 546 547 548 549
}

/*
 * Resume a socket.  If a card is present, verify its CIS against
 * our cached copy.  If they are different, the card has been
 * replaced, and we need to tell the drivers.
 */
static int socket_resume(struct pcmcia_socket *skt)
{
	int ret;

	if (!(skt->state & SOCKET_SUSPEND))
D
Dominik Brodowski 已提交
550
		return -EBUSY;
L
Linus Torvalds 已提交
551 552 553 554 555 556 557 558 559 560 561

	skt->socket = dead_socket;
	skt->ops->init(skt);
	skt->ops->set_socket(skt, &skt->socket);

	if (!(skt->state & SOCKET_PRESENT)) {
		skt->state &= ~SOCKET_SUSPEND;
		return socket_insert(skt);
	}

	ret = socket_setup(skt, resume_delay);
D
Dominik Brodowski 已提交
562
	if (ret == 0) {
L
Linus Torvalds 已提交
563 564 565 566
		/*
		 * FIXME: need a better check here for cardbus cards.
		 */
		if (verify_cis_cache(skt) != 0) {
567
			dev_dbg(&skt->dev, "cis mismatch - different card\n");
L
Linus Torvalds 已提交
568 569 570 571 572 573 574 575 576 577
			socket_remove_drivers(skt);
			destroy_cis_cache(skt);
			/*
			 * Workaround: give DS time to schedule removal.
			 * Remove me once the 100ms delay is eliminated
			 * in ds.c
			 */
			msleep(200);
			send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
		} else {
578
			dev_dbg(&skt->dev, "cis matches cache\n");
L
Linus Torvalds 已提交
579 580 581 582 583 584 585 586
			send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
		}
	} else {
		socket_shutdown(skt);
	}

	skt->state &= ~SOCKET_SUSPEND;

D
Dominik Brodowski 已提交
587
	return 0;
L
Linus Torvalds 已提交
588 589 590 591
}

static void socket_remove(struct pcmcia_socket *skt)
{
592 593
	dev_printk(KERN_NOTICE, &skt->dev,
		   "pccard: card ejected from slot %d\n", skt->sock);
L
Linus Torvalds 已提交
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
	socket_shutdown(skt);
}

/*
 * Process a socket card detect status change.
 *
 * If we don't have a card already present, delay the detect event for
 * about 20ms (to be on the safe side) before reading the socket status.
 *
 * Some i82365-based systems send multiple SS_DETECT events during card
 * insertion, and the "card present" status bit seems to bounce.  This
 * will probably be true with GPIO-based card detection systems after
 * the product has aged.
 */
static void socket_detect_change(struct pcmcia_socket *skt)
{
	if (!(skt->state & SOCKET_SUSPEND)) {
		int status;

		if (!(skt->state & SOCKET_PRESENT))
			msleep(20);

		skt->ops->get_status(skt, &status);
		if ((skt->state & SOCKET_PRESENT) &&
		     !(status & SS_DETECT))
			socket_remove(skt);
		if (!(skt->state & SOCKET_PRESENT) &&
		    (status & SS_DETECT))
			socket_insert(skt);
	}
}

static int pccardd(void *__skt)
{
	struct pcmcia_socket *skt = __skt;
	int ret;

	skt->thread = current;
	skt->socket = dead_socket;
	skt->ops->init(skt);
	skt->ops->set_socket(skt, &skt->socket);

	/* register with the device core */
637
	ret = device_register(&skt->dev);
L
Linus Torvalds 已提交
638
	if (ret) {
639 640
		dev_printk(KERN_WARNING, &skt->dev,
			   "PCMCIA: unable to register socket\n");
L
Linus Torvalds 已提交
641
		skt->thread = NULL;
642 643
		complete(&skt->thread_done);
		return 0;
L
Linus Torvalds 已提交
644
	}
645 646 647
	ret = pccard_sysfs_add_socket(&skt->dev);
	if (ret)
		dev_warn(&skt->dev, "err %d adding socket attributes\n", ret);
L
Linus Torvalds 已提交
648

649 650
	complete(&skt->thread_done);

651
	set_freezable();
L
Linus Torvalds 已提交
652 653 654 655 656 657 658 659 660 661 662 663
	for (;;) {
		unsigned long flags;
		unsigned int events;

		set_current_state(TASK_INTERRUPTIBLE);

		spin_lock_irqsave(&skt->thread_lock, flags);
		events = skt->thread_events;
		skt->thread_events = 0;
		spin_unlock_irqrestore(&skt->thread_lock, flags);

		if (events) {
664
			mutex_lock(&skt->skt_mutex);
L
Linus Torvalds 已提交
665 666 667 668 669 670 671 672
			if (events & SS_DETECT)
				socket_detect_change(skt);
			if (events & SS_BATDEAD)
				send_event(skt, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
			if (events & SS_BATWARN)
				send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
			if (events & SS_READY)
				send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
673
			mutex_unlock(&skt->skt_mutex);
L
Linus Torvalds 已提交
674 675 676
			continue;
		}

677
		if (kthread_should_stop())
L
Linus Torvalds 已提交
678
			break;
679 680 681

		schedule();
		try_to_freeze();
L
Linus Torvalds 已提交
682
	}
683 684 685
	/* make sure we are running before we exit */
	set_current_state(TASK_RUNNING);

L
Linus Torvalds 已提交
686
	/* remove from the device core */
687
	pccard_sysfs_remove_socket(&skt->dev);
688
	device_unregister(&skt->dev);
L
Linus Torvalds 已提交
689

690
	return 0;
L
Linus Torvalds 已提交
691 692 693 694 695 696 697 698
}

/*
 * Yenta (at least) probes interrupts before registering the socket and
 * starting the handler thread.
 */
void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
{
699
	unsigned long flags;
700
	dev_dbg(&s->dev, "parse_events: events %08x\n", events);
L
Linus Torvalds 已提交
701
	if (s->thread) {
702
		spin_lock_irqsave(&s->thread_lock, flags);
L
Linus Torvalds 已提交
703
		s->thread_events |= events;
704
		spin_unlock_irqrestore(&s->thread_lock, flags);
L
Linus Torvalds 已提交
705

C
Christoph Hellwig 已提交
706
		wake_up_process(s->thread);
L
Linus Torvalds 已提交
707 708
	}
} /* pcmcia_parse_events */
D
Dominik Brodowski 已提交
709
EXPORT_SYMBOL(pcmcia_parse_events);
L
Linus Torvalds 已提交
710 711 712 713 714 715 716


/* register pcmcia_callback */
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
{
        int ret = 0;

717 718
	/* s->skt_mutex also protects s->callback */
	mutex_lock(&s->skt_mutex);
L
Linus Torvalds 已提交
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733

	if (c) {
		/* registration */
		if (s->callback) {
			ret = -EBUSY;
			goto err;
		}

		s->callback = c;

		if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT)
			send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
	} else
		s->callback = NULL;
 err:
734
	mutex_unlock(&s->skt_mutex);
L
Linus Torvalds 已提交
735 736 737 738 739 740

	return ret;
}
EXPORT_SYMBOL(pccard_register_pcmcia);


D
Dominik Brodowski 已提交
741 742 743 744
/* I'm not sure which "reset" function this is supposed to use,
 * but for now, it uses the low-level interface's reset, not the
 * CIS register.
 */
L
Linus Torvalds 已提交
745

746
int pcmcia_reset_card(struct pcmcia_socket *skt)
L
Linus Torvalds 已提交
747 748
{
	int ret;
D
Dominik Brodowski 已提交
749

750
	dev_dbg(&skt->dev, "resetting socket\n");
L
Linus Torvalds 已提交
751

752
	mutex_lock(&skt->skt_mutex);
L
Linus Torvalds 已提交
753 754
	do {
		if (!(skt->state & SOCKET_PRESENT)) {
755
			dev_dbg(&skt->dev, "can't reset, not present\n");
D
Dominik Brodowski 已提交
756
			ret = -ENODEV;
L
Linus Torvalds 已提交
757 758 759
			break;
		}
		if (skt->state & SOCKET_SUSPEND) {
760
			dev_dbg(&skt->dev, "can't reset, suspended\n");
D
Dominik Brodowski 已提交
761
			ret = -EBUSY;
L
Linus Torvalds 已提交
762 763 764
			break;
		}
		if (skt->state & SOCKET_CARDBUS) {
765
			dev_dbg(&skt->dev, "can't reset, is cardbus\n");
766
			ret = -EPERM;
L
Linus Torvalds 已提交
767 768 769 770 771 772
			break;
		}

		ret = send_event(skt, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
		if (ret == 0) {
			send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
773 774
			if (skt->callback)
				skt->callback->suspend(skt);
D
Dominik Brodowski 已提交
775
			if (socket_reset(skt) == 0) {
L
Linus Torvalds 已提交
776
				send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
777 778 779
				if (skt->callback)
					skt->callback->resume(skt);
			}
L
Linus Torvalds 已提交
780 781
		}

D
Dominik Brodowski 已提交
782
		ret = 0;
L
Linus Torvalds 已提交
783
	} while (0);
784
	mutex_unlock(&skt->skt_mutex);
L
Linus Torvalds 已提交
785 786 787

	return ret;
} /* reset_card */
788
EXPORT_SYMBOL(pcmcia_reset_card);
L
Linus Torvalds 已提交
789 790


D
Dominik Brodowski 已提交
791 792 793
/* These shut down or wake up a socket.  They are sort of user
 * initiated versions of the APM suspend and resume actions.
 */
L
Linus Torvalds 已提交
794 795 796
int pcmcia_suspend_card(struct pcmcia_socket *skt)
{
	int ret;
D
Dominik Brodowski 已提交
797

798
	dev_dbg(&skt->dev, "suspending socket\n");
L
Linus Torvalds 已提交
799

800
	mutex_lock(&skt->skt_mutex);
L
Linus Torvalds 已提交
801 802
	do {
		if (!(skt->state & SOCKET_PRESENT)) {
D
Dominik Brodowski 已提交
803
			ret = -ENODEV;
L
Linus Torvalds 已提交
804 805 806
			break;
		}
		if (skt->state & SOCKET_CARDBUS) {
807
			ret = -EPERM;
L
Linus Torvalds 已提交
808 809
			break;
		}
810 811 812 813 814
		if (skt->callback) {
			ret = skt->callback->suspend(skt);
			if (ret)
				break;
		}
L
Linus Torvalds 已提交
815 816
		ret = socket_suspend(skt);
	} while (0);
817
	mutex_unlock(&skt->skt_mutex);
L
Linus Torvalds 已提交
818 819 820

	return ret;
} /* suspend_card */
D
Dominik Brodowski 已提交
821 822
EXPORT_SYMBOL(pcmcia_suspend_card);

L
Linus Torvalds 已提交
823 824 825 826 827

int pcmcia_resume_card(struct pcmcia_socket *skt)
{
	int ret;
    
828
	dev_dbg(&skt->dev, "waking up socket\n");
L
Linus Torvalds 已提交
829

830
	mutex_lock(&skt->skt_mutex);
L
Linus Torvalds 已提交
831 832
	do {
		if (!(skt->state & SOCKET_PRESENT)) {
D
Dominik Brodowski 已提交
833
			ret = -ENODEV;
L
Linus Torvalds 已提交
834 835 836
			break;
		}
		if (skt->state & SOCKET_CARDBUS) {
837
			ret = -EPERM;
L
Linus Torvalds 已提交
838 839 840
			break;
		}
		ret = socket_resume(skt);
841 842
		if (!ret && skt->callback)
			skt->callback->resume(skt);
L
Linus Torvalds 已提交
843
	} while (0);
844
	mutex_unlock(&skt->skt_mutex);
L
Linus Torvalds 已提交
845 846 847

	return ret;
} /* resume_card */
D
Dominik Brodowski 已提交
848
EXPORT_SYMBOL(pcmcia_resume_card);
L
Linus Torvalds 已提交
849 850


D
Dominik Brodowski 已提交
851
/* These handle user requests to eject or insert a card. */
L
Linus Torvalds 已提交
852 853 854 855
int pcmcia_eject_card(struct pcmcia_socket *skt)
{
	int ret;
    
856
	dev_dbg(&skt->dev, "user eject request\n");
L
Linus Torvalds 已提交
857

858
	mutex_lock(&skt->skt_mutex);
L
Linus Torvalds 已提交
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
	do {
		if (!(skt->state & SOCKET_PRESENT)) {
			ret = -ENODEV;
			break;
		}

		ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
		if (ret != 0) {
			ret = -EINVAL;
			break;
		}

		socket_remove(skt);
		ret = 0;
	} while (0);
874
	mutex_unlock(&skt->skt_mutex);
L
Linus Torvalds 已提交
875 876 877

	return ret;
} /* eject_card */
D
Dominik Brodowski 已提交
878 879
EXPORT_SYMBOL(pcmcia_eject_card);

L
Linus Torvalds 已提交
880 881 882 883 884

int pcmcia_insert_card(struct pcmcia_socket *skt)
{
	int ret;

885
	dev_dbg(&skt->dev, "user insert request\n");
L
Linus Torvalds 已提交
886

887
	mutex_lock(&skt->skt_mutex);
L
Linus Torvalds 已提交
888 889 890 891 892
	do {
		if (skt->state & SOCKET_PRESENT) {
			ret = -EBUSY;
			break;
		}
D
Dominik Brodowski 已提交
893
		if (socket_insert(skt) == -ENODEV) {
L
Linus Torvalds 已提交
894 895 896 897 898
			ret = -ENODEV;
			break;
		}
		ret = 0;
	} while (0);
899
	mutex_unlock(&skt->skt_mutex);
L
Linus Torvalds 已提交
900 901 902

	return ret;
} /* insert_card */
D
Dominik Brodowski 已提交
903 904
EXPORT_SYMBOL(pcmcia_insert_card);

L
Linus Torvalds 已提交
905

906 907
static int pcmcia_socket_uevent(struct device *dev,
				struct kobj_uevent_env *env)
908 909 910
{
	struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev);

911
	if (add_uevent_var(env, "SOCKET_NO=%u", s->sock))
912 913 914 915 916
		return -ENOMEM;

	return 0;
}

L
Linus Torvalds 已提交
917

918 919 920 921 922 923 924 925
static struct completion pcmcia_unload;

static void pcmcia_release_socket_class(struct class *data)
{
	complete(&pcmcia_unload);
}


L
Linus Torvalds 已提交
926 927
struct class pcmcia_socket_class = {
	.name = "pcmcia_socket",
928 929
	.dev_uevent = pcmcia_socket_uevent,
	.dev_release = pcmcia_release_socket,
930
	.class_release = pcmcia_release_socket_class,
L
Linus Torvalds 已提交
931 932 933 934 935 936
};
EXPORT_SYMBOL(pcmcia_socket_class);


static int __init init_pcmcia_cs(void)
{
937
	init_completion(&pcmcia_unload);
938
	return class_register(&pcmcia_socket_class);
L
Linus Torvalds 已提交
939 940 941 942
}

static void __exit exit_pcmcia_cs(void)
{
D
Dominik Brodowski 已提交
943
	class_unregister(&pcmcia_socket_class);
944
	wait_for_completion(&pcmcia_unload);
L
Linus Torvalds 已提交
945 946 947 948 949
}

subsys_initcall(init_pcmcia_cs);
module_exit(exit_pcmcia_cs);