📄 synclink_cs.c
字号:
/* * 1st function defined in .text section. Calling this function in * init_module() followed by a breakpoint allows a remote debugger * (gdb) to get the .text address for the add-symbol-file command. * This allows remote debugging of dynamically loadable modules. */static void* mgslpc_get_text_ptr(void){ return mgslpc_get_text_ptr;}/** * line discipline callback wrappers * * The wrappers maintain line discipline references * while calling into the line discipline. * * ldisc_flush_buffer - flush line discipline receive buffers * ldisc_receive_buf - pass receive data to line discipline */static void ldisc_flush_buffer(struct tty_struct *tty){ struct tty_ldisc *ld = tty_ldisc_ref(tty); if (ld) { if (ld->flush_buffer) ld->flush_buffer(tty); tty_ldisc_deref(ld); }}static void ldisc_receive_buf(struct tty_struct *tty, const __u8 *data, char *flags, int count){ struct tty_ldisc *ld; if (!tty) return; ld = tty_ldisc_ref(tty); if (ld) { if (ld->receive_buf) ld->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); }}static dev_link_t *mgslpc_attach(void){ MGSLPC_INFO *info; dev_link_t *link; client_reg_t client_reg; int ret; if (debug_level >= DEBUG_LEVEL_INFO) printk("mgslpc_attach\n"); info = (MGSLPC_INFO *)kmalloc(sizeof(MGSLPC_INFO), GFP_KERNEL); if (!info) { printk("Error can't allocate device instance data\n"); return NULL; } memset(info, 0, sizeof(MGSLPC_INFO)); info->magic = MGSLPC_MAGIC; INIT_WORK(&info->task, bh_handler, info); info->max_frame_size = 4096; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->lock); spin_lock_init(&info->netlock); memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); info->idle_mode = HDLC_TXIDLE_FLAGS; info->imra_value = 0xffff; info->imrb_value = 0xffff; info->pim_value = 0xff; link = &info->link; link->priv = info; /* Initialize the dev_link_t structure */ /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; link->conf.Attributes = 0; link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; /* Register with Card Services */ link->next = dev_list; dev_list = link; client_reg.dev_info = &dev_info; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; ret = pcmcia_register_client(&link->handle, &client_reg); if (ret != CS_SUCCESS) { cs_error(link->handle, RegisterClient, ret); mgslpc_detach(link); return NULL; } mgslpc_add_device(info); return link;}/* Card has been inserted. */#define CS_CHECK(fn, ret) \do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)static void mgslpc_config(dev_link_t *link){ client_handle_t handle = link->handle; MGSLPC_INFO *info = link->priv; tuple_t tuple; cisparse_t parse; int last_fn, last_ret; u_char buf[64]; config_info_t conf; cistpl_cftable_entry_t dflt = { 0 }; cistpl_cftable_entry_t *cfg; if (debug_level >= DEBUG_LEVEL_INFO) printk("mgslpc_config(0x%p)\n", link); /* read CONFIG tuple to find its configuration registers */ tuple.DesiredTuple = CISTPL_CONFIG; tuple.Attributes = 0; tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; /* Configure card */ link->state |= DEV_CONFIG; /* Look up the current Vcc */ CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); link->conf.Vcc = conf.Vcc; /* get CIS configuration entry */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); cfg = &(parse.cftable_entry); CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; if (cfg->index == 0) goto cs_failed; link->conf.ConfigIndex = cfg->index; link->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ link->io.NumPorts1 = 0; if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.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; CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io)); } link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 8; link->conf.Present = PRESENT_OPTION; link->irq.Attributes |= IRQ_HANDLE_PRESENT; link->irq.Handler = mgslpc_isr; link->irq.Instance = info; CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); info->io_base = link->io.BasePort1; info->irq_level = link->irq.AssignedIRQ; /* add to linked list of devices */ sprintf(info->node.dev_name, "mgslpc0"); info->node.major = info->node.minor = 0; link->dev = &info->node; printk(KERN_INFO "%s: index 0x%02x:", info->node.dev_name, link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); printk("\n"); link->state &= ~DEV_CONFIG_PENDING; return;cs_failed: cs_error(link->handle, last_fn, last_ret); mgslpc_release((u_long)link);}/* Card has been removed. * Unregister device and release PCMCIA configuration. * If device is open, postpone until it is closed. */static void mgslpc_release(u_long arg){ dev_link_t *link = (dev_link_t *)arg; if (debug_level >= DEBUG_LEVEL_INFO) printk("mgslpc_release(0x%p)\n", link); /* Unlink the device chain */ link->dev = NULL; link->state &= ~DEV_CONFIG; pcmcia_release_configuration(link->handle); if (link->io.NumPorts1) pcmcia_release_io(link->handle, &link->io); if (link->irq.AssignedIRQ) pcmcia_release_irq(link->handle, &link->irq); if (link->state & DEV_STALE_LINK) mgslpc_detach(link);}static void mgslpc_detach(dev_link_t *link){ dev_link_t **linkp; if (debug_level >= DEBUG_LEVEL_INFO) printk("mgslpc_detach(0x%p)\n", link); /* find device */ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) if (*linkp == link) break; if (*linkp == NULL) return; if (link->state & DEV_CONFIG) { /* device is configured/active, mark it so when * release() is called a proper detach() occurs. */ if (debug_level >= DEBUG_LEVEL_INFO) printk(KERN_DEBUG "synclinkpc: detach postponed, '%s' " "still locked\n", link->dev->dev_name); link->state |= DEV_STALE_LINK; return; } /* Break the link with Card Services */ if (link->handle) pcmcia_deregister_client(link->handle); /* Unlink device structure, and free it */ *linkp = link->next; mgslpc_remove_device((MGSLPC_INFO *)link->priv);}static int mgslpc_event(event_t event, int priority, event_callback_args_t *args){ dev_link_t *link = args->client_data; MGSLPC_INFO *info = link->priv; if (debug_level >= DEBUG_LEVEL_INFO) printk("mgslpc_event(0x%06x)\n", event); switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { ((MGSLPC_INFO *)link->priv)->stop = 1; mgslpc_release((u_long)link); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; mgslpc_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: /* Mark the device as stopped, to block IO until later */ info->stop = 1; if (link->state & DEV_CONFIG) pcmcia_release_configuration(link->handle); break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) pcmcia_request_configuration(link->handle, &link->conf); info->stop = 0; break; } return 0;}static inline int mgslpc_paranoia_check(MGSLPC_INFO *info, char *name, const char *routine){#ifdef MGSLPC_PARANOIA_CHECK static const char *badmagic = "Warning: bad magic number for mgsl struct (%s) in %s\n"; static const char *badinfo = "Warning: null mgslpc_info for (%s) in %s\n"; if (!info) { printk(badinfo, name, routine); return 1; } if (info->magic != MGSLPC_MAGIC) { printk(badmagic, name, routine); return 1; }#else if (!info) return 1;#endif return 0;}#define CMD_RXFIFO BIT7 // release current rx FIFO#define CMD_RXRESET BIT6 // receiver reset#define CMD_RXFIFO_READ BIT5#define CMD_START_TIMER BIT4#define CMD_TXFIFO BIT3 // release current tx FIFO#define CMD_TXEOM BIT1 // transmit end message#define CMD_TXRESET BIT0 // transmit resetstatic BOOLEAN wait_command_complete(MGSLPC_INFO *info, unsigned char channel) { int i = 0; /* wait for command completion */ while (read_reg(info, (unsigned char)(channel+STAR)) & BIT2) { udelay(1); if (i++ == 1000) return FALSE; } return TRUE;}static void issue_command(MGSLPC_INFO *info, unsigned char channel, unsigned char cmd) { wait_command_complete(info, channel); write_reg(info, (unsigned char) (channel + CMDR), cmd);}static void tx_pause(struct tty_struct *tty){ MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; if (mgslpc_paranoia_check(info, tty->name, "tx_pause")) return; if (debug_level >= DEBUG_LEVEL_INFO) printk("tx_pause(%s)\n",info->device_name); spin_lock_irqsave(&info->lock,flags); if (info->tx_enabled) tx_stop(info); spin_unlock_irqrestore(&info->lock,flags);}static void tx_release(struct tty_struct *tty){ MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; if (mgslpc_paranoia_check(info, tty->name, "tx_release")) return; if (debug_level >= DEBUG_LEVEL_INFO) printk("tx_release(%s)\n",info->device_name); spin_lock_irqsave(&info->lock,flags); if (!info->tx_enabled) tx_start(info); spin_unlock_irqrestore(&info->lock,flags);}/* Return next bottom half action to perform. * or 0 if nothing to do. */static int bh_action(MGSLPC_INFO *info){ unsigned long flags; int rc = 0; spin_lock_irqsave(&info->lock,flags); if (info->pending_bh & BH_RECEIVE) { info->pending_bh &= ~BH_RECEIVE; rc = BH_RECEIVE; } else if (info->pending_bh & BH_TRANSMIT) { info->pending_bh &= ~BH_TRANSMIT; rc = BH_TRANSMIT; } else if (info->pending_bh & BH_STATUS) { info->pending_bh &= ~BH_STATUS; rc = BH_STATUS; } if (!rc) { /* Mark BH routine as complete */ info->bh_running = 0; info->bh_requested = 0; } spin_unlock_irqrestore(&info->lock,flags); return rc;}void bh_handler(void* Context){ MGSLPC_INFO *info = (MGSLPC_INFO*)Context; int action; if (!info) return; if (debug_level >= DEBUG_LEVEL_BH) printk( "%s(%d):bh_handler(%s) entry\n", __FILE__,__LINE__,info->device_name); info->bh_running = 1; while((action = bh_action(info)) != 0) { /* Process work item */ if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):bh_handler() work item action=%d\n", __FILE__,__LINE__,action); switch (action) { case BH_RECEIVE: while(rx_get_frame(info)); break; case BH_TRANSMIT: bh_transmit(info); break; case BH_STATUS: bh_status(info); break; default: /* unknown work item ID */ printk("Unknown work item ID=%08X!\n", action); break; } } if (debug_level >= DEBUG_LEVEL_BH) printk( "%s(%d):bh_handler(%s) exit\n", __FILE__,__LINE__,info->device_name);}void bh_transmit(MGSLPC_INFO *info){ struct tty_struct *tty = info->tty; if (debug_level >= DEBUG_LEVEL_BH) printk("bh_transmit() entry on %s\n", info->device_name); if (tty) { tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); }}void bh_status(MGSLPC_INFO *info){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -