cs.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,183 行 · 第 1/4 页

C
2,183
字号
/*======================================================================    Kernel Card Services -- core services    cs.c 1.271 2000/10/02 20:27:49        The contents of this file are subject to the Mozilla Public    License Version 1.1 (the "License"); you may not use this file    except in compliance with the License. You may obtain a copy of    the License at http://www.mozilla.org/MPL/    Software distributed under the License is distributed on an "AS    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or    implied. See the License for the specific language governing    rights and limitations under the License.    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.    Alternatively, the contents of this file may be used under the    terms of the GNU General Public License version 2 (the "GPL"), in which    case the provisions of the GPL are applicable instead of the    above.  If you wish to allow the use of your version of this file    only under the terms of the GPL and not to allow others to use    your version of this file under the MPL, indicate your decision    by deleting the provisions above and replace them with the notice    and other provisions required by the GPL.  If you do not delete    the provisions above, a recipient may use your version of this    file under either the MPL or the GPL.    ======================================================================*/#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/config.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/pci.h>#include <linux/device.h>#include <linux/suspend.h>#include <asm/system.h>#include <asm/irq.h>#define IN_CARD_SERVICES#include <pcmcia/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/ss.h>#include <pcmcia/cs.h>#include <pcmcia/bulkmem.h>#include <pcmcia/cistpl.h>#include <pcmcia/cisreg.h>#include "cs_internal.h"#ifdef CONFIG_PCI#define PCI_OPT " [pci]"#else#define PCI_OPT ""#endif#ifdef CONFIG_CARDBUS#define CB_OPT " [cardbus]"#else#define CB_OPT ""#endif#ifdef CONFIG_PM#define PM_OPT " [pm]"#else#define PM_OPT ""#endif#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && !defined(CONFIG_PM)#define OPTIONS " none"#else#define OPTIONS PCI_OPT CB_OPT PM_OPT#endifstatic const char *release = "Linux Kernel Card Services";static const char *options = "options: " OPTIONS;/*====================================================================*//* Module parameters */MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");MODULE_DESCRIPTION("Linux Kernel Card Services\noptions:" OPTIONS);MODULE_LICENSE("Dual MPL/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 *//* Access speed for IO windows */INT_MODULE_PARM(io_speed,	0);		/* ns */#ifdef DEBUGstatic int pc_debug;module_param(pc_debug, int, 0644);int cs_debug_level(int level){	return pc_debug > level;}#endif/*====================================================================*/socket_state_t dead_socket = {	.csc_mask	= SS_DETECT,};/* List of all sockets, protected by a rwsem */LIST_HEAD(pcmcia_socket_list);DECLARE_RWSEM(pcmcia_socket_list_rwsem);/*====================================================================    Low-level PC Card interface drivers need to register with Card    Services using these calls.    ======================================================================*//** * socket drivers are expected to use the following callbacks in their  * .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);int pcmcia_socket_dev_suspend(struct device *dev, u32 state){	struct pcmcia_socket *socket;	down_read(&pcmcia_socket_list_rwsem);	list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {		if (socket->dev.dev != dev)			continue;		down(&socket->skt_sem);		socket_suspend(socket);		up(&socket->skt_sem);	}	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) {		if (socket->dev.dev != dev)			continue;		down(&socket->skt_sem);		socket_resume(socket);		up(&socket->skt_sem);	}	up_read(&pcmcia_socket_list_rwsem);	return 0;}EXPORT_SYMBOL(pcmcia_socket_dev_resume);static void pcmcia_release_socket(struct class_device *class_dev){	struct pcmcia_socket *socket = class_get_devdata(class_dev);	client_t *client;	while (socket->clients) {		client = socket->clients;		socket->clients = socket->clients->next;		kfree(client);	}	complete(&socket->socket_released);}static int pccardd(void *__skt);/** * pcmcia_register_socket - add a new pcmcia socket device */int pcmcia_register_socket(struct pcmcia_socket *socket){	int ret;	if (!socket || !socket->ops || !socket->dev.dev)		return -EINVAL;	cs_dbg(socket, 0, "pcmcia_register_socket(0x%p)\n", socket->ops);	/* try to obtain a socket number [yes, it gets ugly if we	 * register more than 2^sizeof(unsigned int) pcmcia 	 * sockets... but the socket number is deprecated 	 * 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);	/* set proper values in socket->dev */	socket->dev.class_data = socket;	socket->dev.class = &pcmcia_socket_class;	snprintf(socket->dev.class_id, BUS_ID_SIZE, "pcmcia_socket%u", socket->sock);	/* base address = 0, map = 0 */	socket->cis_mem.flags = 0;	socket->cis_mem.speed = cis_speed;	socket->erase_busy.next = socket->erase_busy.prev = &socket->erase_busy;	INIT_LIST_HEAD(&socket->cis_cache);	spin_lock_init(&socket->lock);	init_completion(&socket->socket_released);	init_completion(&socket->thread_done);	init_waitqueue_head(&socket->thread_wait);	init_MUTEX(&socket->skt_sem);	spin_lock_init(&socket->thread_lock);	ret = kernel_thread(pccardd, socket, CLONE_KERNEL);	if (ret < 0)		goto err;	wait_for_completion(&socket->thread_done);	if(!socket->thread) {		printk(KERN_WARNING "PCMCIA: warning: socket thread for socket %p did not start\n", socket);		return -EIO;	}	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 */void pcmcia_unregister_socket(struct pcmcia_socket *socket){	if (!socket)		return;	cs_dbg(socket, 0, "pcmcia_unregister_socket(0x%p)\n", socket->ops);	if (socket->thread) {		init_completion(&socket->thread_done);		socket->thread = NULL;		wake_up(&socket->thread_wait);		wait_for_completion(&socket->thread_done);	}	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 */	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);/*======================================================================    Shutdown_Socket() and setup_socket() are scheduled using add_timer    calls by the main event handler when card insertion and removal    events are received.  Shutdown_Socket() unconfigures a socket and    turns off socket power.  Setup_socket() turns on socket power    and resets the socket, in two stages.======================================================================*/static void free_regions(memory_handle_t *list){    memory_handle_t tmp;    while (*list != NULL) {	tmp = *list;	*list = tmp->info.next;	tmp->region_magic = 0;	kfree(tmp);    }}static int send_event(struct pcmcia_socket *s, event_t event, int priority);static void shutdown_socket(struct pcmcia_socket *s){    client_t **c;        cs_dbg(s, 1, "shutdown_socket\n");    /* 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);    if (s->fake_cis) {	kfree(s->fake_cis);	s->fake_cis = NULL;    }#ifdef CONFIG_CARDBUS    cb_free(s);#endif    s->functions = 0;    if (s->config) {	kfree(s->config);	s->config = NULL;    }    for (c = &s->clients; *c; ) {	if ((*c)->state & CLIENT_UNBOUND) {	    client_t *d = *c;	    *c = (*c)->next;	    kfree(d);	} else {	    c = &((*c)->next);	}    }    free_regions(&s->a_region);    free_regions(&s->c_region);    {	int status;	s->ops->get_status(s, &status);	if (status & SS_POWERON) {		printk(KERN_ERR "PCMCIA: socket %p: *** DANGER *** unable to remove socket power\n", s);	}    }} /* shutdown_socket *//*======================================================================    The central event handler.  Send_event() sends an event to all    valid clients.  Parse_events() interprets the event bits from    a card status change report.  Do_shutdown() handles the high    priority stuff associated with a card removal.    ======================================================================*/static int send_event(struct pcmcia_socket *s, event_t event, int priority){    client_t *client = s->clients;    int ret;    cs_dbg(s, 1, "send_event(event %d, pri %d)\n",	   event, priority);    ret = 0;    if (s->state & SOCKET_CARDBUS)	    return 0;    for (; client; client = client->next) { 	if (client->state & (CLIENT_UNBOUND|CLIENT_STALE))	    continue;	if (client->EventMask & event) {	    ret = EVENT(client, event, priority);	    if (ret != 0)		return ret;	}    }    return ret;} /* send_event */static void socket_remove_drivers(struct pcmcia_socket *skt){	client_t *client;	cs_dbg(skt, 4, "remove_drivers\n");	send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);	for (client = skt->clients; client; client = client->next)		if (!(client->Attributes & INFO_MASTER_CLIENT))			client->state |= CLIENT_STALE;}static void socket_shutdown(struct pcmcia_socket *skt){	cs_dbg(skt, 4, "shutdown\n");	socket_remove_drivers(skt);	skt->state &= SOCKET_INUSE|SOCKET_PRESENT;	msleep(shutdown_delay * 10);	skt->state &= SOCKET_INUSE;	shutdown_socket(skt);}static int socket_reset(struct pcmcia_socket *skt){	int status, i;	cs_dbg(skt, 4, "reset\n");	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))			return CS_NO_CARD;		if (status & SS_READY)			return CS_SUCCESS;		msleep(unreset_check * 10);	}	cs_err(skt, "time out after reset.\n");	return CS_GENERAL_FAILURE;}static int socket_setup(struct pcmcia_socket *skt, int initial_delay){	int status, i;	cs_dbg(skt, 4, "setup\n");	skt->ops->get_status(skt, &status);	if (!(status & SS_DETECT))		return CS_NO_CARD;	msleep(initial_delay * 10);	for (i = 0; i < 100; i++) {		skt->ops->get_status(skt, &status);		if (!(status & SS_DETECT))			return CS_NO_CARD;		if (!(status & SS_PENDING))			break;		msleep(100);	}	if (status & SS_PENDING) {		cs_err(skt, "voltage interrogation timed out.\n");		return CS_GENERAL_FAILURE;	}	if (status & SS_CARDBUS) {		skt->state |= SOCKET_CARDBUS;#ifndef CONFIG_CARDBUS		cs_err(skt, "cardbus cards are not supported.\n");		return CS_BAD_TYPE;#endif	}	/*	 * 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 {		cs_err(skt, "unsupported voltage key.\n");		return CS_BAD_TYPE;	}	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)) {		cs_err(skt, "unable to apply power.\n");		return CS_BAD_TYPE;	}	return socket_reset(skt);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?