⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ds.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * ds.c -- 16-bit PCMCIA core support * * 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 * (C) 2003 - 2006	Dominik Brodowski */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/list.h>#include <linux/delay.h>#include <linux/workqueue.h>#include <linux/crc32.h>#include <linux/firmware.h>#include <linux/kref.h>#include <linux/dma-mapping.h>#define IN_CARD_SERVICES#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cistpl.h>#include <pcmcia/ds.h>#include <pcmcia/ss.h>#include "cs_internal.h"#include "ds_internal.h"/*====================================================================*//* Module parameters */MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");MODULE_DESCRIPTION("PCMCIA Driver Services");MODULE_LICENSE("GPL");#ifdef DEBUGint ds_pc_debug;module_param_named(pc_debug, ds_pc_debug, int, 0644);#define ds_dbg(lvl, fmt, arg...) do {				\	if (ds_pc_debug > (lvl))					\		printk(KERN_DEBUG "ds: " fmt , ## arg);		\} while (0)#else#define ds_dbg(lvl, fmt, arg...) do { } while (0)#endifspinlock_t pcmcia_dev_list_lock;/*====================================================================*//* code which was in cs.c before *//* String tables for error messages */typedef struct lookup_t {    int key;    char *msg;} lookup_t;static const lookup_t error_table[] = {    { CS_SUCCESS,		"Operation succeeded" },    { CS_BAD_ADAPTER,		"Bad adapter" },    { CS_BAD_ATTRIBUTE, 	"Bad attribute", },    { CS_BAD_BASE,		"Bad base address" },    { CS_BAD_EDC,		"Bad EDC" },    { CS_BAD_IRQ,		"Bad IRQ" },    { CS_BAD_OFFSET,		"Bad offset" },    { CS_BAD_PAGE,		"Bad page number" },    { CS_READ_FAILURE,		"Read failure" },    { CS_BAD_SIZE,		"Bad size" },    { CS_BAD_SOCKET,		"Bad socket" },    { CS_BAD_TYPE,		"Bad type" },    { CS_BAD_VCC,		"Bad Vcc" },    { CS_BAD_VPP,		"Bad Vpp" },    { CS_BAD_WINDOW,		"Bad window" },    { CS_WRITE_FAILURE,		"Write failure" },    { CS_NO_CARD,		"No card present" },    { CS_UNSUPPORTED_FUNCTION,	"Usupported function" },    { CS_UNSUPPORTED_MODE,	"Unsupported mode" },    { CS_BAD_SPEED,		"Bad speed" },    { CS_BUSY,			"Resource busy" },    { CS_GENERAL_FAILURE,	"General failure" },    { CS_WRITE_PROTECTED,	"Write protected" },    { CS_BAD_ARG_LENGTH,	"Bad argument length" },    { CS_BAD_ARGS,		"Bad arguments" },    { CS_CONFIGURATION_LOCKED,	"Configuration locked" },    { CS_IN_USE,		"Resource in use" },    { CS_NO_MORE_ITEMS,		"No more items" },    { CS_OUT_OF_RESOURCE,	"Out of resource" },    { CS_BAD_HANDLE,		"Bad handle" },    { CS_BAD_TUPLE,		"Bad CIS tuple" }};static const lookup_t service_table[] = {    { AccessConfigurationRegister,	"AccessConfigurationRegister" },    { AddSocketServices,		"AddSocketServices" },    { AdjustResourceInfo,		"AdjustResourceInfo" },    { CheckEraseQueue,			"CheckEraseQueue" },    { CloseMemory,			"CloseMemory" },    { DeregisterClient,			"DeregisterClient" },    { DeregisterEraseQueue,		"DeregisterEraseQueue" },    { GetCardServicesInfo,		"GetCardServicesInfo" },    { GetClientInfo,			"GetClientInfo" },    { GetConfigurationInfo,		"GetConfigurationInfo" },    { GetEventMask,			"GetEventMask" },    { GetFirstClient,			"GetFirstClient" },    { GetFirstRegion,			"GetFirstRegion" },    { GetFirstTuple,			"GetFirstTuple" },    { GetNextClient,			"GetNextClient" },    { GetNextRegion,			"GetNextRegion" },    { GetNextTuple,			"GetNextTuple" },    { GetStatus,			"GetStatus" },    { GetTupleData,			"GetTupleData" },    { MapMemPage,			"MapMemPage" },    { ModifyConfiguration,		"ModifyConfiguration" },    { ModifyWindow,			"ModifyWindow" },    { OpenMemory,			"OpenMemory" },    { ParseTuple,			"ParseTuple" },    { ReadMemory,			"ReadMemory" },    { RegisterClient,			"RegisterClient" },    { RegisterEraseQueue,		"RegisterEraseQueue" },    { RegisterMTD,			"RegisterMTD" },    { ReleaseConfiguration,		"ReleaseConfiguration" },    { ReleaseIO,			"ReleaseIO" },    { ReleaseIRQ,			"ReleaseIRQ" },    { ReleaseWindow,			"ReleaseWindow" },    { RequestConfiguration,		"RequestConfiguration" },    { RequestIO,			"RequestIO" },    { RequestIRQ,			"RequestIRQ" },    { RequestSocketMask,		"RequestSocketMask" },    { RequestWindow,			"RequestWindow" },    { ResetCard,			"ResetCard" },    { SetEventMask,			"SetEventMask" },    { ValidateCIS,			"ValidateCIS" },    { WriteMemory,			"WriteMemory" },    { BindDevice,			"BindDevice" },    { BindMTD,				"BindMTD" },    { ReportError,			"ReportError" },    { SuspendCard,			"SuspendCard" },    { ResumeCard,			"ResumeCard" },    { EjectCard,			"EjectCard" },    { InsertCard,			"InsertCard" },    { ReplaceCIS,			"ReplaceCIS" }};static int pcmcia_report_error(struct pcmcia_device *p_dev, error_info_t *err){	int i;	char *serv;	if (!p_dev)		printk(KERN_NOTICE);	else		printk(KERN_NOTICE "%s: ", p_dev->dev.bus_id);	for (i = 0; i < ARRAY_SIZE(service_table); i++)		if (service_table[i].key == err->func)			break;	if (i < ARRAY_SIZE(service_table))		serv = service_table[i].msg;	else		serv = "Unknown service number";	for (i = 0; i < ARRAY_SIZE(error_table); i++)		if (error_table[i].key == err->retcode)			break;	if (i < ARRAY_SIZE(error_table))		printk("%s: %s\n", serv, error_table[i].msg);	else		printk("%s: Unknown error code %#x\n", serv, err->retcode);	return CS_SUCCESS;} /* report_error *//* end of code which was in cs.c before *//*======================================================================*/void cs_error(struct pcmcia_device *p_dev, int func, int ret){	error_info_t err = { func, ret };	pcmcia_report_error(p_dev, &err);}EXPORT_SYMBOL(cs_error);static void pcmcia_check_driver(struct pcmcia_driver *p_drv){	struct pcmcia_device_id *did = p_drv->id_table;	unsigned int i;	u32 hash;	if (!p_drv->probe || !p_drv->remove)		printk(KERN_DEBUG "pcmcia: %s lacks a requisite callback "		       "function\n", p_drv->drv.name);	while (did && did->match_flags) {		for (i=0; i<4; i++) {			if (!did->prod_id[i])				continue;			hash = crc32(0, did->prod_id[i], strlen(did->prod_id[i]));			if (hash == did->prod_id_hash[i])				continue;			printk(KERN_DEBUG "pcmcia: %s: invalid hash for "			       "product string \"%s\": is 0x%x, should "			       "be 0x%x\n", p_drv->drv.name, did->prod_id[i],			       did->prod_id_hash[i], hash);			printk(KERN_DEBUG "pcmcia: see "				"Documentation/pcmcia/devicetable.txt for "				"details\n");		}		did++;	}	return;}/*======================================================================*/struct pcmcia_dynid {	struct list_head 		node;	struct pcmcia_device_id 	id;};/** * pcmcia_store_new_id - add a new PCMCIA device ID to this driver and re-probe devices * @driver: target device driver * @buf: buffer for scanning device ID data * @count: input size * * Adds a new dynamic PCMCIA device ID to this driver, * and causes the driver to probe for all devices again. */static ssize_tpcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count){	struct pcmcia_dynid *dynid;	struct pcmcia_driver *pdrv = to_pcmcia_drv(driver);	__u16 match_flags, manf_id, card_id;	__u8 func_id, function, device_no;	__u32 prod_id_hash[4] = {0, 0, 0, 0};	int fields=0;	int retval = 0;	fields = sscanf(buf, "%hx %hx %hx %hhx %hhx %hhx %x %x %x %x",			&match_flags, &manf_id, &card_id, &func_id, &function, &device_no,			&prod_id_hash[0], &prod_id_hash[1], &prod_id_hash[2], &prod_id_hash[3]);	if (fields < 6)		return -EINVAL;	dynid = kzalloc(sizeof(struct pcmcia_dynid), GFP_KERNEL);	if (!dynid)		return -ENOMEM;	INIT_LIST_HEAD(&dynid->node);	dynid->id.match_flags = match_flags;	dynid->id.manf_id = manf_id;	dynid->id.card_id = card_id;	dynid->id.func_id = func_id;	dynid->id.function = function;	dynid->id.device_no = device_no;	memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4);	spin_lock(&pdrv->dynids.lock);	list_add_tail(&pdrv->dynids.list, &dynid->node);	spin_unlock(&pdrv->dynids.lock);	if (get_driver(&pdrv->drv)) {		retval = driver_attach(&pdrv->drv);		put_driver(&pdrv->drv);	}	if (retval)		return retval;	return count;}static DRIVER_ATTR(new_id, S_IWUSR, NULL, pcmcia_store_new_id);static voidpcmcia_free_dynids(struct pcmcia_driver *drv){	struct pcmcia_dynid *dynid, *n;	spin_lock(&drv->dynids.lock);	list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {		list_del(&dynid->node);		kfree(dynid);	}	spin_unlock(&drv->dynids.lock);}static intpcmcia_create_newid_file(struct pcmcia_driver *drv){	int error = 0;	if (drv->probe != NULL)		error = sysfs_create_file(&drv->drv.kobj,					  &driver_attr_new_id.attr);	return error;}/** * pcmcia_register_driver - register a PCMCIA driver with the bus core * @driver: the &driver being registered * * Registers a PCMCIA driver with the PCMCIA bus core. */int pcmcia_register_driver(struct pcmcia_driver *driver){	int error;	if (!driver)		return -EINVAL;	pcmcia_check_driver(driver);	/* initialize common fields */	driver->drv.bus = &pcmcia_bus_type;	driver->drv.owner = driver->owner;	spin_lock_init(&driver->dynids.lock);	INIT_LIST_HEAD(&driver->dynids.list);	ds_dbg(3, "registering driver %s\n", driver->drv.name);	error = driver_register(&driver->drv);	if (error < 0)		return error;	error = pcmcia_create_newid_file(driver);	if (error)		driver_unregister(&driver->drv);	return error;}EXPORT_SYMBOL(pcmcia_register_driver);/** * pcmcia_unregister_driver - unregister a PCMCIA driver with the bus core * @driver: the &driver being unregistered */void pcmcia_unregister_driver(struct pcmcia_driver *driver){	ds_dbg(3, "unregistering driver %s\n", driver->drv.name);	driver_unregister(&driver->drv);	pcmcia_free_dynids(driver);}EXPORT_SYMBOL(pcmcia_unregister_driver);/* pcmcia_device handling */struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev){	struct device *tmp_dev;	tmp_dev = get_device(&p_dev->dev);	if (!tmp_dev)		return NULL;	return to_pcmcia_dev(tmp_dev);}void pcmcia_put_dev(struct pcmcia_device *p_dev){	if (p_dev)		put_device(&p_dev->dev);}static void pcmcia_release_function(struct kref *ref){	struct config_t *c = container_of(ref, struct config_t, ref);	ds_dbg(1, "releasing config_t\n");	kfree(c);}static void pcmcia_release_dev(struct device *dev){	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);	ds_dbg(1, "releasing device %s\n", p_dev->dev.bus_id);	pcmcia_put_socket(p_dev->socket);	kfree(p_dev->devname);	kref_put(&p_dev->function_config->ref, pcmcia_release_function);	kfree(p_dev);}static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc){	if (!s->pcmcia_state.device_add_pending) {		ds_dbg(1, "scheduling to add %s secondary"		       " device to %d\n", mfc ? "mfc" : "pfc", s->sock);		s->pcmcia_state.device_add_pending = 1;		s->pcmcia_state.mfc_pfc = mfc;		schedule_work(&s->device_add);	}	return;}static int pcmcia_device_probe(struct device * dev){	struct pcmcia_device *p_dev;	struct pcmcia_driver *p_drv;	struct pcmcia_device_id *did;	struct pcmcia_socket *s;	cistpl_config_t cis_config;	int ret = 0;	dev = get_device(dev);	if (!dev)		return -ENODEV;	p_dev = to_pcmcia_dev(dev);	p_drv = to_pcmcia_drv(dev->driver);	s = p_dev->socket;	ds_dbg(1, "trying to bind %s to %s\n", p_dev->dev.bus_id,	       p_drv->drv.name);	if ((!p_drv->probe) || (!p_dev->function_config) ||	    (!try_module_get(p_drv->owner))) {		ret = -EINVAL;		goto put_dev;	}	/* set up some more device information */	ret = pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_CONFIG,				&cis_config);	if (!ret) {		p_dev->conf.ConfigBase = cis_config.base;		p_dev->conf.Present = cis_config.rmask[0];	} else {		printk(KERN_INFO "pcmcia: could not parse base and rmask0 of CIS\n");		p_dev->conf.ConfigBase = 0;		p_dev->conf.Present = 0;	}	ret = p_drv->probe(p_dev);	if (ret) {		ds_dbg(1, "binding %s to %s failed with %d\n",		       p_dev->dev.bus_id, p_drv->drv.name, ret);		goto put_module;	}	/* handle pseudo multifunction devices:	 * there are at most two pseudo multifunction devices.	 * if we're matching against the first, schedule a	 * call which will then check whether there are two	 * pseudo devices, and if not, add the second one.	 */	did = p_dev->dev.driver_data;	if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&	    (p_dev->socket->device_count == 1) && (p_dev->device_no == 0))		pcmcia_add_device_later(p_dev->socket, 0); put_module:	if (ret)		module_put(p_drv->owner); put_dev:	if (ret)		put_device(dev);	return (ret);}/* * Removes a PCMCIA card from the device tree and socket list. */static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover){	struct pcmcia_device	*p_dev;	struct pcmcia_device	*tmp;	unsigned long		flags;	ds_dbg(2, "pcmcia_card_remove(%d) %s\n", s->sock,	       leftover ? leftover->devname : "");	if (!leftover)		s->device_count = 0;	else		s->device_count = 1;	/* unregister all pcmcia_devices registered with this socket, except leftover */	list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {		if (p_dev == leftover)			continue;		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);		list_del(&p_dev->socket_device_list);		p_dev->_removed=1;		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);		ds_dbg(2, "unregistering device %s\n", p_dev->dev.bus_id);		device_unregister(&p_dev->dev);	}	return;}static int pcmcia_device_remove(struct device * dev){	struct pcmcia_device *p_dev;	struct pcmcia_driver *p_drv;	struct pcmcia_device_id *did;	int i;	p_dev = to_pcmcia_dev(dev);	p_drv = to_pcmcia_drv(dev->driver);	ds_dbg(1, "removing device %s\n", p_dev->dev.bus_id);	/* If we're removing the primary module driving a

⌨️ 快捷键说明

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