📄 synclink_cs.c
字号:
static int irq_list[4] = { -1 };MODULE_PARM(irq_mask, "i");MODULE_PARM(irq_list, "1-4i");MODULE_PARM(break_on_load,"i");MODULE_PARM(ttymajor,"i");MODULE_PARM(cuamajor,"i");MODULE_PARM(debug_level,"i");MODULE_PARM(maxframe,"1-" __MODULE_STRING(MAX_DEVICE_COUNT) "i");MODULE_LICENSE("GPL");static char *driver_name = "SyncLink PC Card driver";static char *driver_version = "$Revision: 3.4 $";static struct tty_driver serial_driver, callout_driver;static int serial_refcount;/* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256static void mgslpc_change_params(MGSLPC_INFO *info);static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout);static struct tty_struct *serial_table[MAX_DEVICE_COUNT];static struct termios *serial_termios[MAX_DEVICE_COUNT];static struct termios *serial_termios_locked[MAX_DEVICE_COUNT];#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif/* PCMCIA prototypes */static void mgslpc_config(dev_link_t *link);static void mgslpc_release(u_long arg);static int mgslpc_event(event_t event, int priority, event_callback_args_t *args);static dev_link_t *mgslpc_attach(void);static void mgslpc_detach(dev_link_t *);static dev_info_t dev_info = "synclink_cs";static dev_link_t *dev_list = NULL;static void cs_error(client_handle_t handle, int func, int ret){ error_info_t err = { func, ret }; CardServices(ReportError, handle, &err);}/* * 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);static void* mgslpc_get_text_ptr() {return mgslpc_get_text_ptr;}static dev_link_t *mgslpc_attach(void){ MGSLPC_INFO *info; dev_link_t *link; client_reg_t client_reg; int ret, i; 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; info->task.sync = 0; info->task.routine = bh_handler; info->task.data = 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 */ link->release.function = &mgslpc_release; link->release.data = (u_long)link; /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; if (irq_list[0] == -1) link->irq.IRQInfo2 = irq_mask; else for (i = 0; i < 4; i++) link->irq.IRQInfo2 |= 1 << irq_list[i]; 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.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.event_handler = &mgslpc_event; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != CS_SUCCESS) { cs_error(link->handle, RegisterClient, ret); mgslpc_detach(link); return NULL; } mgslpc_add_device(info); memset(serial_table,0,sizeof(struct tty_struct*)*MAX_DEVICE_COUNT); memset(serial_termios,0,sizeof(struct termios*)*MAX_DEVICE_COUNT); memset(serial_termios_locked,0,sizeof(struct termios*)*MAX_DEVICE_COUNT); info->callout_termios = callout_driver.init_termios; info->normal_termios = serial_driver.init_termios; return link;}/* Card has been inserted. */#define CS_CHECK(fn, args...) \while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failedstatic 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, handle, &tuple); CS_CHECK(GetTupleData, handle, &tuple); CS_CHECK(ParseTuple, 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, handle, &conf); link->conf.Vcc = conf.Vcc; /* get CIS configuration entry */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, handle, &tuple); cfg = &(parse.cftable_entry); CS_CHECK(GetTupleData, handle, &tuple); CS_CHECK(ParseTuple, 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, 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, link->handle, &link->irq); CS_CHECK(RequestConfiguration, 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); if (link->open) { if (debug_level >= DEBUG_LEVEL_INFO) printk("synclink_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; return; } /* Unlink the device chain */ link->dev = NULL; link->state &= ~DEV_CONFIG; CardServices(ReleaseConfiguration, link->handle); if (link->io.NumPorts1) CardServices(ReleaseIO, link->handle, &link->io); if (link->irq.AssignedIRQ) CardServices(ReleaseIRQ, 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) CardServices(DeregisterClient, 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; mod_timer(&link->release, jiffies + HZ/20); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; info->bus = args->bus; 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) CardServices(ReleaseConfiguration, link->handle); break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) CardServices(RequestConfiguration, link->handle, &link->conf); info->stop = 0; break; } return 0;}static inline int mgslpc_paranoia_check(MGSLPC_INFO *info, kdev_t device, 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, kdevname(device), routine); return 1; } if (info->magic != MGSLPC_MAGIC) { printk(badmagic, kdevname(device), routine); 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; unsigned char status; /* wait for command completion */ while ((status = 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->device, "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->device, "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. */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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -