📄 cf_io.c
字号:
/*
* File cfio.c
**/
#include "cfio_io.h"
#define __NO_VERSION__
int *register_cf_driver(cf_notifier_fn_add, cf_notifier_fn_remove , void *);
void unregister_cf_driver(void);
int cfio_read_cfg_reg(void* priv);
#define CS_ERROR(h,f,r) { \
error_info_t err = {f,r}; \
CardServices(ReportError,h,&err); \
}
/*
The bus_operations pointer is used on platforms for which we need
to use special socket-specific versions of normal IO primitives
(inb, outb, readb, writeb, etc) for card IO.
*/
static void cf_config(dev_link_t * link);
static void cf_release(ulong arg);
static int cf_event(event_t event, int priority, event_callback_args_t * args);
static dev_link_t *cf_attach(void);
static void cf_detach(dev_link_t *);
dev_info_t cfio_dev_info = "mcf25";
dev_link_t *dev_list = NULL;
struct cf_card_rec cardp;
EXPORT_SYMBOL_NOVERS(cardp);
EXPORT_SYMBOL_NOVERS(cfio_dev_info);
typedef struct _if_pcmcia_info_t {
dev_link_t link;
dev_node_t node;
int stop;
struct bus_operations *bus;
struct net_device *eth_dev;
} if_pcmcia_info_t;
/*
MODULE PARAMETERS
*/
EXPORT_SYMBOL_NOVERS(register_cf_driver);
EXPORT_SYMBOL_NOVERS(unregister_cf_driver);
EXPORT_SYMBOL_NOVERS(cfio_read_cfg_reg);
#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
MODULE_PARM(interrupt_steer, "1-4i");
#ifdef ORIGINAL_CODE
INT_MODULE_PARM(free_ports, 0);
#endif
INT_MODULE_PARM(irq_mask, 0xdeb8);
/* Module Variables */
static int interrupt_steer[4] = { -1 };
void break1(void){}
dev_link_t *cf_attach(void)
{
int i, status;
if_pcmcia_info_t *ifinfo;
dev_link_t *link;
client_reg_t client_reg;
printk("Entering cf_attach()\n");
/* Allocate space for PCMCIA information */
if (!(ifinfo = kmalloc(sizeof(if_pcmcia_info_t), GFP_KERNEL))) {
return NULL;
}
memset(ifinfo, 0, sizeof(if_pcmcia_info_t));
link = &ifinfo->link;
link->priv = ifinfo;
init_timer(&link->release);
link->release.function = &cf_release;
link->release.data = (ulong) link;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
/* Is the IRQ specified by the user? */
if (interrupt_steer[0] == -1) {
/* No, find the required IRQ */
link->irq.IRQInfo2 = irq_mask;
} else {
/* Yes, create the mask based on user specified IRQ's */
for (i = 0; i < 4; i++) {
link->irq.IRQInfo2 |= 1 << interrupt_steer[i];
}
}
/* Handler will be installed later */
link->irq.Handler = NULL;
link->conf.Attributes = 0;
link->conf.Vcc = 50;
link->conf.Vpp1 = 0;
link->conf.Vpp2 = 0;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.ConfigIndex = 1;
link->conf.Present = PRESENT_OPTION;
link->next = dev_list;
dev_list = link;
client_reg.dev_info = &cfio_dev_info;
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
client_reg.EventMask = CS_EVENT_CARD_INSERTION |
CS_EVENT_CARD_REMOVAL | CS_EVENT_CARD_RESET;
client_reg.event_handler = &cf_event;
client_reg.event_callback_args.client_data = link;
client_reg.Version = 0x0326;
printk("Before registering the client\n");
if ((status = CardServices(RegisterClient, &link->handle,
&client_reg)) != CS_SUCCESS) {
printk("Registering the client failed\n");
CS_ERROR(link->handle, RegisterClient, status);
cf_detach(link);
return NULL;
}
printk("Leaving cf_attach()\n");
return link;
} /* cf_attach */
void cf_detach(dev_link_t * link)
{
dev_link_t **p;
for (p = &dev_list; *p; p = &(*p)->next)
if (*p == link)
break;
if (*p == NULL)
return;
del_timer_sync(&link->release);
if (((if_pcmcia_info_t *) link->priv)->eth_dev) {
printk("Before calling wlan_remove function\n");
//pwlanpriv = NULL;
cardp.remove(&cardp);
printk("After calling wlan_remove function\n");
}
if (link->state & DEV_CONFIG) {
cf_release((u32) link);
}
((if_pcmcia_info_t *) link->priv)->eth_dev = NULL;
if (link->handle) {
CardServices(ResetCard, link->handle);
CardServices(DeregisterClient, link->handle);
}
*p = link->next;
/* This points to the parent if_pcmcia_info_t struct */
if (link->priv)
kfree(link->priv);
#ifdef CF_MEM_CHECK
DisplayMemCounters();
#endif
} /* cf_detach */
void cf_config(dev_link_t * link)
{
client_handle_t handle = link->handle;
if_pcmcia_info_t *dev = link->priv;
tuple_t tuple;
cisparse_t parse;
cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
u8 buf[64];
config_info_t conf;
tuple.DesiredTuple = CISTPL_CONFIG;
tuple.Attributes = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
tuple.TupleOffset = 0;
if (CardServices(GetFirstTuple, handle, &tuple))
goto onerror;
if (CardServices(GetTupleData, handle, &tuple))
goto onerror;
if (CardServices(ParseTuple, handle, &tuple, &parse))
goto onerror;
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
link->state |= DEV_CONFIG;
if (CardServices(GetConfigurationInfo, handle, &conf))
goto onerror;
link->conf.Vcc = conf.Vcc;
/*
The Configuration table consists of a series of configuration table
entry tuples. Each entry consists of up to seven data structures that
describe operational characteristsics of the PC card.
*/
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
if (CardServices(GetFirstTuple, handle, &tuple))
goto onerror;
if (CardServices(GetTupleData, handle, &tuple) != CS_SUCCESS)
goto onerror;
if (CardServices(ParseTuple, handle, &tuple, &parse) != CS_SUCCESS)
goto onerror;
link->conf.ConfigIndex = cfg->index;
/* Interrupt request description */
if (cfg->irq.IRQInfo1)
link->conf.Attributes |= CONF_ENABLE_IRQ;
/* IO Address space description */
link->io.NumPorts1 = link->io.NumPorts2 = 0;
if ((cfg->io.nwin > 0)) {
cistpl_io_t *io;
if (cfg->io.nwin)
io = &cfg->io;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
if (!(io->flags & CISTPL_IO_8BIT))
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
if (!(io->flags & CISTPL_IO_16BIT))
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
link->io.BasePort1 = io->win[0].base;
link->io.NumPorts1 = io->win[0].len;
if (io->nwin > 1) {
link->io.Attributes2 = link->io.Attributes1;
link->io.BasePort2 = io->win[1].base;
link->io.NumPorts2 = io->win[1].len;
}
if (CardServices(RequestIO, link->handle, &link->io)
!= CS_SUCCESS) {
CardServices(ReleaseIO, link->handle, &link->io);
printk("Request IO Error !!\n");
goto onerror;
}
}
if (link->conf.Attributes & CONF_ENABLE_IRQ)
if (CardServices(RequestIRQ, link->handle, &link->irq))
goto onerror;
if (CardServices(RequestConfiguration, link->handle, &link->conf))
goto onerror;
cardp.irq = link->irq.AssignedIRQ;
cardp.port = link->io.BasePort1;
printk("BasePort1=0x%x, AssignedIRQ=%d\n",
link->io.BasePort1, link->irq.AssignedIRQ);
if (!(cardp.add(&cardp))) {
printk("Call to cardp.add failed\n");
goto onerror;
}
printk("After calling wlan_add_card function\n");
((if_pcmcia_info_t *) link->priv)->eth_dev = cardp.eth_dev;
if (!((if_pcmcia_info_t *) link->priv)->eth_dev)
goto onerror;
strcpy(dev->node.dev_name, cfio_dev_info);
strcpy(dev->node.dev_name, cardp.eth_dev->name);
dev->node.major = dev->node.minor = 0;
link->dev = &dev->node;
link->state &= ~DEV_CONFIG_PENDING;
return;
onerror:
printk("card configuration failed...calling cf_release function\n");
cf_release((u32) link);
link->state &= ~DEV_CONFIG_PENDING;
cardp.flag = 1;
} /* cf_config */
void cf_release(ulong arg)
{
dev_link_t *link = (dev_link_t *) arg;
link->dev = NULL;
CardServices(ReleaseConfiguration, link->handle);
if (link->io.NumPorts1)
CardServices(ReleaseIO, link->handle, &link->io);
if (link->irq.AssignedIRQ)
CardServices(ReleaseIRQ, link->handle, &link->irq);
link->state &= ~DEV_CONFIG;
} /* cf_release */
int cf_event(event_t event, int priority, event_callback_args_t *args)
{
dev_link_t *link = args->client_data;
if_pcmcia_info_t *dev = link->priv;
switch (event) {
case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
dev->bus = args->bus;
cf_config(link);
break;
case CS_EVENT_CARD_REMOVAL:
link->state &= ~DEV_PRESENT;
printk("card removal event detected\n");
if (link->state & DEV_CONFIG) {
((if_pcmcia_info_t *) link->priv)->stop = 1;
printk("Before calling release function\n");
mod_timer(&link->release, jiffies + HZ / 20);
printk("After calling release function\n");
}
break;
case CS_EVENT_CARD_RESET:
if (link->state & DEV_CONFIG) {
CardServices(RequestConfiguration,
link->handle, &link->conf);
}
dev->stop = 0;
break;
}
return 0;
} /* cf_event */
int *register_cf_driver(cf_notifier_fn_add add, cf_notifier_fn_remove remove,
void *arg)
{
servinfo_t serv;
cardp.add = add;
cardp.remove = remove;
cardp.host_int_mask = 0;
CardServices(GetCardServicesInfo, &serv);
if (serv.Revision != CS_RELEASE_CODE) {
return NULL;
}
printk("Before calling register_pccard_driver\n");
register_pccard_driver(&cfio_dev_info, &cf_attach, &cf_detach);
printk("After calling register_pccard_driver\n");
return (int *) &cardp;
}
void unregister_cf_driver()
{
unregister_pccard_driver(&cfio_dev_info);
cf_detach(dev_list);
while (dev_list != NULL) {
del_timer(&dev_list->release);
if (dev_list->state & DEV_CONFIG)
cf_release((u32) dev_list);
}
}
int cfio_init_module(void)
{
return 0;
}
void cfio_cleanup_module(void)
{
}
int cfio_read_cfg_reg(void* priv)
{
conf_reg_t reg;
reg.Function = 0;
reg.Action = CS_READ;
reg.Offset = 0;
reg.Value = 0;
// access the bus to wake up the device
// O2 Micro CardBus bridge has problems of with this. So we read
// a configuration register instead.
// cf_IsCmdReady(priv);
// There is no way of getting the current handle easily! We simply
// get the first one from the list. This could be a problem when you
// have more than one instance!
CardServices(AccessConfigurationRegister, dev_list->handle, ®);
return 0;
}
module_init(cfio_init_module);
module_exit(cfio_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -