From 0b2cf5c8402bb313c0054b2cd7df5ca50f250a1d Mon Sep 17 00:00:00 2001 From: Daeseok Youn Date: Fri, 31 Oct 2014 10:20:37 +0900 Subject: staging: dgap: re-arrange functions for removing forward declarations Re-arrange the functions for removing forward declarations. Signed-off-by: Daeseok Youn Tested-by: Mark Hounschell Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/staging/dgap/dgap.c b/drivers/staging/dgap/dgap.c index ed356f1..293dc33 100644 --- a/drivers/staging/dgap/dgap.c +++ b/drivers/staging/dgap/dgap.c @@ -65,145 +65,6 @@ #include "dgap.h" -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Digi International, http://www.digi.com"); -MODULE_DESCRIPTION("Driver for the Digi International EPCA PCI based product line"); -MODULE_SUPPORTED_DEVICE("dgap"); - -static int dgap_start(void); -static void dgap_stop(void); -static void dgap_init_globals(void); -static struct board_t *dgap_found_board(struct pci_dev *pdev, int id, - int boardnum); -static void dgap_cleanup_board(struct board_t *brd); -static void dgap_poll_handler(ulong dummy); -static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static void dgap_remove_one(struct pci_dev *dev); -static int dgap_remap(struct board_t *brd); -static void dgap_unmap(struct board_t *brd); -static irqreturn_t dgap_intr(int irq, void *voidbrd); - -static int dgap_tty_open(struct tty_struct *tty, struct file *file); -static void dgap_tty_close(struct tty_struct *tty, struct file *file); -static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, - struct channel_t *ch); -static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg); -static int dgap_tty_digigeta(struct channel_t *ch, - struct digi_t __user *retinfo); -static int dgap_tty_digiseta(struct channel_t *ch, struct board_t *bd, - struct un_t *un, struct digi_t __user *new_info); -static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo); -static int dgap_tty_digisetedelay(struct channel_t *ch, struct board_t *bd, - struct un_t *un, int __user *new_info); -static int dgap_tty_write_room(struct tty_struct *tty); -static int dgap_tty_chars_in_buffer(struct tty_struct *tty); -static void dgap_tty_start(struct tty_struct *tty); -static void dgap_tty_stop(struct tty_struct *tty); -static void dgap_tty_throttle(struct tty_struct *tty); -static void dgap_tty_unthrottle(struct tty_struct *tty); -static void dgap_tty_flush_chars(struct tty_struct *tty); -static void dgap_tty_flush_buffer(struct tty_struct *tty); -static void dgap_tty_hangup(struct tty_struct *tty); -static int dgap_wait_for_drain(struct tty_struct *tty); -static int dgap_set_modem_info(struct channel_t *ch, struct board_t *bd, - struct un_t *un, unsigned int command, - unsigned int __user *value); -static int dgap_get_modem_info(struct channel_t *ch, - unsigned int __user *value); -static int dgap_tty_digisetcustombaud(struct channel_t *ch, struct board_t *bd, - struct un_t *un, int __user *new_info); -static int dgap_tty_digigetcustombaud(struct channel_t *ch, struct un_t *un, - int __user *retinfo); -static int dgap_tty_tiocmget(struct tty_struct *tty); -static int dgap_tty_tiocmset(struct tty_struct *tty, unsigned int set, - unsigned int clear); -static int dgap_tty_send_break(struct tty_struct *tty, int msec); -static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout); -static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, - int count); -static void dgap_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios); -static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c); -static void dgap_tty_send_xchar(struct tty_struct *tty, char ch); - -static int dgap_tty_register(struct board_t *brd); -static void dgap_tty_unregister(struct board_t *brd); -static int dgap_tty_init(struct board_t *); -static void dgap_tty_free(struct board_t *); -static void dgap_cleanup_tty(struct board_t *); -static void dgap_carrier(struct channel_t *ch); -static void dgap_input(struct channel_t *ch); - -/* - * Our function prototypes from dgap_fep5 - */ -static void dgap_cmdw_ext(struct channel_t *ch, u16 cmd, u16 word, uint ncmds); -static int dgap_event(struct board_t *bd); - -static void dgap_poll_tasklet(unsigned long data); -static void dgap_cmdb(struct channel_t *ch, u8 cmd, u8 byte1, - u8 byte2, uint ncmds); -static void dgap_cmdw(struct channel_t *ch, u8 cmd, u16 word, uint ncmds); -static void dgap_wmove(struct channel_t *ch, char *buf, uint cnt); -static int dgap_param(struct channel_t *ch, struct board_t *bd, u32 un_type); -static void dgap_parity_scan(struct channel_t *ch, unsigned char *cbuf, - unsigned char *fbuf, int *len); -static uint dgap_get_custom_baud(struct channel_t *ch); -static void dgap_firmware_reset_port(struct channel_t *ch); - -/* - * Function prototypes from dgap_parse.c. - */ -static int dgap_gettok(char **in); -static char *dgap_getword(char **in); -static int dgap_checknode(struct cnode *p); - -/* - * Function prototypes from dgap_sysfs.h - */ -static void dgap_create_ports_sysfiles(struct board_t *bd); -static void dgap_remove_ports_sysfiles(struct board_t *bd); - -static int dgap_create_driver_sysfiles(struct pci_driver *); -static void dgap_remove_driver_sysfiles(struct pci_driver *); - -static void dgap_create_tty_sysfs(struct un_t *un, struct device *c); -static void dgap_remove_tty_sysfs(struct device *c); - -/* - * Function prototypes from dgap_parse.h - */ -static int dgap_parsefile(char **in); -static struct cnode *dgap_find_config(int type, int bus, int slot); -static uint dgap_config_get_num_prts(struct board_t *bd); -static char *dgap_create_config_string(struct board_t *bd, char *string); -static uint dgap_config_get_useintr(struct board_t *bd); -static uint dgap_config_get_altpin(struct board_t *bd); - -static void dgap_do_bios_load(struct board_t *brd, const u8 *ubios, int len); -static void dgap_do_fep_load(struct board_t *brd, const u8 *ufep, int len); -#ifdef DIGI_CONCENTRATORS_SUPPORTED -static void dgap_do_conc_load(struct board_t *brd, u8 *uaddr, int len); -#endif -static int dgap_alloc_flipbuf(struct board_t *brd); -static void dgap_free_flipbuf(struct board_t *brd); -static int dgap_request_irq(struct board_t *brd); -static void dgap_free_irq(struct board_t *brd); - -static void dgap_get_vpd(struct board_t *brd); -static void dgap_do_reset_board(struct board_t *brd); -static int dgap_test_bios(struct board_t *brd); -static int dgap_test_fep(struct board_t *brd); -static int dgap_tty_register_ports(struct board_t *brd); -static int dgap_firmware_load(struct pci_dev *pdev, int card_type, - struct board_t *brd); -static void dgap_cleanup_nodes(void); - -static void dgap_cleanup_module(void); - -module_exit(dgap_cleanup_module); - /* * File operations permitted on Control/Management major. */ @@ -298,13 +159,6 @@ static struct board_id dgap_ids[] = { {0,} /* 0 terminated list. */ }; -static struct pci_driver dgap_driver = { - .name = "dgap", - .probe = dgap_init_one, - .id_table = dgap_pci_tbl, - .remove = dgap_remove_one, -}; - struct firmware_info { u8 *conf_name; /* dgap.conf */ u8 *bios_name; /* BIOS filename */ @@ -367,29 +221,6 @@ static struct ktermios dgap_default_termios = { .c_line = 0, }; -static const struct tty_operations dgap_tty_ops = { - .open = dgap_tty_open, - .close = dgap_tty_close, - .write = dgap_tty_write, - .write_room = dgap_tty_write_room, - .flush_buffer = dgap_tty_flush_buffer, - .chars_in_buffer = dgap_tty_chars_in_buffer, - .flush_chars = dgap_tty_flush_chars, - .ioctl = dgap_tty_ioctl, - .set_termios = dgap_tty_set_termios, - .stop = dgap_tty_stop, - .start = dgap_tty_start, - .throttle = dgap_tty_throttle, - .unthrottle = dgap_tty_unthrottle, - .hangup = dgap_tty_hangup, - .put_char = dgap_tty_put_char, - .tiocmget = dgap_tty_tiocmget, - .tiocmset = dgap_tty_tiocmset, - .break_ctl = dgap_tty_send_break, - .wait_until_sent = dgap_tty_wait_until_sent, - .send_xchar = dgap_tty_send_xchar -}; - /* * Our needed internal static variables from dgap_parse.c */ @@ -457,3104 +288,2909 @@ static struct toklist dgap_tlist[] = { { 0, NULL } }; -/************************************************************************ - * - * Driver load/unload functions - * - ************************************************************************/ /* - * init_module() - * - * Module load. This is where it all starts. + * dgap_sindex: much like index(), but it looks for a match of any character in + * the group, and returns that position. If the first character is a ^, then + * this will match the first occurrence not in that group. */ -static int dgap_init_module(void) +static char *dgap_sindex(char *string, char *group) { - int rc; - - pr_info("%s, Digi International Part Number %s\n", DG_NAME, DG_PART); - - rc = dgap_start(); - if (rc) - return rc; - - rc = pci_register_driver(&dgap_driver); - if (rc) - goto err_stop; - - rc = dgap_create_driver_sysfiles(&dgap_driver); - if (rc) - goto err_unregister; - - dgap_driver_state = DRIVER_READY; + char *ptr; - return 0; + if (!string || !group) + return NULL; -err_unregister: - pci_unregister_driver(&dgap_driver); -err_stop: - dgap_stop(); + if (*group == '^') { + group++; + for (; *string; string++) { + for (ptr = group; *ptr; ptr++) { + if (*ptr == *string) + break; + } + if (*ptr == '\0') + return string; + } + } else { + for (; *string; string++) { + for (ptr = group; *ptr; ptr++) { + if (*ptr == *string) + return string; + } + } + } - return rc; + return NULL; } -module_init(dgap_init_module); /* - * Start of driver. + * get a word from the input stream, also keep track of current line number. + * words are separated by whitespace. */ -static int dgap_start(void) +static char *dgap_getword(char **in) { - int rc; - unsigned long flags; - struct device *device; - - /* - * make sure that the globals are - * init'd before we do anything else - */ - dgap_init_globals(); - - dgap_numboards = 0; - - pr_info("For the tools package please visit http://www.digi.com\n"); + char *ret_ptr = *in; - /* - * Register our base character device into the kernel. - */ + char *ptr = dgap_sindex(*in, " \t\n"); - /* - * Register management/dpa devices - */ - rc = register_chrdev(DIGI_DGAP_MAJOR, "dgap", &dgap_board_fops); - if (rc < 0) - return rc; + /* If no word found, return null */ + if (!ptr) + return NULL; - dgap_class = class_create(THIS_MODULE, "dgap_mgmt"); - if (IS_ERR(dgap_class)) { - rc = PTR_ERR(dgap_class); - goto failed_class; - } + /* Mark new location for our buffer */ + *ptr = '\0'; + *in = ptr + 1; - device = device_create(dgap_class, NULL, - MKDEV(DIGI_DGAP_MAJOR, 0), - NULL, "dgap_mgmt"); - if (IS_ERR(device)) { - rc = PTR_ERR(device); - goto failed_device; + /* Eat any extra spaces/tabs/newlines that might be present */ + while (*in && **in && ((**in == ' ') || + (**in == '\t') || + (**in == '\n'))) { + **in = '\0'; + *in = *in + 1; } - /* Start the poller */ - spin_lock_irqsave(&dgap_poll_lock, flags); - init_timer(&dgap_poll_timer); - dgap_poll_timer.function = dgap_poll_handler; - dgap_poll_timer.data = 0; - dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick); - dgap_poll_timer.expires = dgap_poll_time; - spin_unlock_irqrestore(&dgap_poll_lock, flags); - - add_timer(&dgap_poll_timer); - - return rc; - -failed_device: - class_destroy(dgap_class); -failed_class: - unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); - return rc; + return ret_ptr; } -static void dgap_stop(void) -{ - unsigned long lock_flags; - spin_lock_irqsave(&dgap_poll_lock, lock_flags); - dgap_poll_stop = 1; - spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); +/* + * Get a token from the input file; return 0 if end of file is reached + */ +static int dgap_gettok(char **in) +{ + char *w; + struct toklist *t; - del_timer_sync(&dgap_poll_timer); + if (strstr(dgap_cword, "board")) { + w = dgap_getword(in); + snprintf(dgap_cword, MAXCWORD, "%s", w); + for (t = dgap_brdtype; t->token != 0; t++) { + if (!strcmp(w, t->string)) + return t->token; + } + } else { + while ((w = dgap_getword(in))) { + snprintf(dgap_cword, MAXCWORD, "%s", w); + for (t = dgap_tlist; t->token != 0; t++) { + if (!strcmp(w, t->string)) + return t->token; + } + } + } - device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0)); - class_destroy(dgap_class); - unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); + return 0; } -static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +/* + * dgap_checknode: see if all the necessary info has been supplied for a node + * before creating the next node. + */ +static int dgap_checknode(struct cnode *p) { - int rc; - struct board_t *brd; - - if (dgap_numboards >= MAXBOARDS) - return -EPERM; + switch (p->type) { + case LNODE: + if (p->u.line.v_speed == 0) { + pr_err("line speed not specified"); + return 1; + } + return 0; - rc = pci_enable_device(pdev); - if (rc) - return -EIO; - - brd = dgap_found_board(pdev, ent->driver_data, dgap_numboards); - if (IS_ERR(brd)) - return PTR_ERR(brd); - - rc = dgap_firmware_load(pdev, ent->driver_data, brd); - if (rc) - goto cleanup_brd; - - rc = dgap_alloc_flipbuf(brd); - if (rc) - goto cleanup_brd; - - rc = dgap_tty_register(brd); - if (rc) - goto free_flipbuf; - - rc = dgap_request_irq(brd); - if (rc) - goto unregister_tty; + case CNODE: + if (p->u.conc.v_speed == 0) { + pr_err("concentrator line speed not specified"); + return 1; + } + if (p->u.conc.v_nport == 0) { + pr_err("number of ports on concentrator not specified"); + return 1; + } + if (p->u.conc.v_id == 0) { + pr_err("concentrator id letter not specified"); + return 1; + } + return 0; - /* - * Do tty device initialization. - */ - rc = dgap_tty_init(brd); - if (rc < 0) - goto free_irq; + case MNODE: + if (p->u.module.v_nport == 0) { + pr_err("number of ports on EBI module not specified"); + return 1; + } + if (p->u.module.v_id == 0) { + pr_err("EBI module id letter not specified"); + return 1; + } + return 0; + } + return 0; +} - rc = dgap_tty_register_ports(brd); - if (rc) - goto tty_free; +/* + * Given a board pointer, returns whether we should use interrupts or not. + */ +static uint dgap_config_get_useintr(struct board_t *bd) +{ + struct cnode *p; - brd->state = BOARD_READY; - brd->dpastatus = BD_RUNNING; + if (!bd) + return 0; - dgap_board[dgap_numboards++] = brd; + for (p = bd->bd_config; p; p = p->next) { + if (p->type == INTRNODE) { + /* + * check for pcxr types. + */ + return p->u.useintr; + } + } + /* If not found, then don't turn on interrupts. */ return 0; - -tty_free: - dgap_tty_free(brd); -free_irq: - dgap_free_irq(brd); -unregister_tty: - dgap_tty_unregister(brd); -free_flipbuf: - dgap_free_flipbuf(brd); -cleanup_brd: - dgap_cleanup_nodes(); - dgap_unmap(brd); - kfree(brd); - - return rc; -} - -static void dgap_remove_one(struct pci_dev *dev) -{ - /* Do Nothing */ } /* - * dgap_cleanup_module() - * - * Module unload. This is where it all ends. + * Given a board pointer, returns whether we turn on altpin or not. */ -static void dgap_cleanup_module(void) +static uint dgap_config_get_altpin(struct board_t *bd) { - unsigned int i; - ulong lock_flags; - - spin_lock_irqsave(&dgap_poll_lock, lock_flags); - dgap_poll_stop = 1; - spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - - /* Turn off poller right away. */ - del_timer_sync(&dgap_poll_timer); - - dgap_remove_driver_sysfiles(&dgap_driver); + struct cnode *p; - device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0)); - class_destroy(dgap_class); - unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); + if (!bd) + return 0; - for (i = 0; i < dgap_numboards; ++i) { - dgap_remove_ports_sysfiles(dgap_board[i]); - dgap_cleanup_tty(dgap_board[i]); - dgap_cleanup_board(dgap_board[i]); + for (p = bd->bd_config; p; p = p->next) { + if (p->type == ANODE) { + /* + * check for pcxr types. + */ + return p->u.altpin; + } } - dgap_cleanup_nodes(); - - if (dgap_numboards) - pci_unregister_driver(&dgap_driver); + /* If not found, then don't turn on interrupts. */ + return 0; } /* - * dgap_cleanup_board() - * - * Free all the memory associated with a board + * Given a specific type of board, if found, detached link and + * returns the first occurrence in the list. */ -static void dgap_cleanup_board(struct board_t *brd) +static struct cnode *dgap_find_config(int type, int bus, int slot) { - unsigned int i; + struct cnode *p, *prev, *prev2, *found; - if (!brd || brd->magic != DGAP_BOARD_MAGIC) - return; + p = &dgap_head; - dgap_free_irq(brd); + while (p->next) { + prev = p; + p = p->next; - tasklet_kill(&brd->helper_tasklet); + if (p->type != BNODE) + continue; - dgap_unmap(brd); + if (p->u.board.type != type) + continue; - /* Free all allocated channels structs */ - for (i = 0; i < MAXPORTS ; i++) - kfree(brd->channels[i]); + if (p->u.board.v_pcibus && + p->u.board.pcibus != bus) + continue; - kfree(brd->flipbuf); - kfree(brd->flipflagbuf); + if (p->u.board.v_pcislot && + p->u.board.pcislot != slot) + continue; - dgap_board[brd->boardnum] = NULL; + found = p; + /* + * Keep walking thru the list till we + * find the next board. + */ + while (p->next) { + prev2 = p; + p = p->next; - kfree(brd); + if (p->type != BNODE) + continue; + + /* + * Mark the end of our 1 board + * chain of configs. + */ + prev2->next = NULL; + + /* + * Link the "next" board to the + * previous board, effectively + * "unlinking" our board from + * the main config. + */ + prev->next = p; + + return found; + } + /* + * It must be the last board in the list. + */ + prev->next = NULL; + return found; + } + return NULL; } /* - * dgap_found_board() - * - * A board has been found, init it. + * Given a board pointer, walks the config link, counting up + * all ports user specified should be on the board. + * (This does NOT mean they are all actually present right now tho) */ -static struct board_t *dgap_found_board(struct pci_dev *pdev, int id, - int boardnum) +static uint dgap_config_get_num_prts(struct board_t *bd) { - struct board_t *brd; - unsigned int pci_irq; - int i; - int ret; - - /* get the board structure and prep it */ - brd = kzalloc(sizeof(struct board_t), GFP_KERNEL); - if (!brd) - return ERR_PTR(-ENOMEM); - - /* store the info for the board we've found */ - brd->magic = DGAP_BOARD_MAGIC; - brd->boardnum = boardnum; - brd->vendor = dgap_pci_tbl[id].vendor; - brd->device = dgap_pci_tbl[id].device; - brd->pdev = pdev; - brd->pci_bus = pdev->bus->number; - brd->pci_slot = PCI_SLOT(pdev->devfn); - brd->name = dgap_ids[id].name; - brd->maxports = dgap_ids[id].maxports; - brd->type = dgap_ids[id].config_type; - brd->dpatype = dgap_ids[id].dpatype; - brd->dpastatus = BD_NOFEP; - init_waitqueue_head(&brd->state_wait); - - spin_lock_init(&brd->bd_lock); - - brd->inhibit_poller = FALSE; - brd->wait_for_bios = 0; - brd->wait_for_fep = 0; + int count = 0; + struct cnode *p; - for (i = 0; i < MAXPORTS; i++) - brd->channels[i] = NULL; + if (!bd) + return 0; - /* store which card & revision we have */ - pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor); - pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice); - pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev); + for (p = bd->bd_config; p; p = p->next) { - pci_irq = pdev->irq; - brd->irq = pci_irq; - - /* get the PCI Base Address Registers */ - - /* Xr Jupiter and EPC use BAR 2 */ - if (brd->device == PCI_DEV_XRJ_DID || brd->device == PCI_DEV_EPCJ_DID) { - brd->membase = pci_resource_start(pdev, 2); - brd->membase_end = pci_resource_end(pdev, 2); - } - /* Everyone else uses BAR 0 */ - else { - brd->membase = pci_resource_start(pdev, 0); - brd->membase_end = pci_resource_end(pdev, 0); - } - - if (!brd->membase) { - ret = -ENODEV; - goto free_brd; + switch (p->type) { + case BNODE: + /* + * check for pcxr types. + */ + if (p->u.board.type > EPCFE) + count += p->u.board.nport; + break; + case CNODE: + count += p->u.conc.nport; + break; + case MNODE: + count += p->u.module.nport; + break; + } } + return count; +} - if (brd->membase & 1) - brd->membase &= ~3; - else - brd->membase &= ~15; - - /* - * On the PCI boards, there is no IO space allocated - * The I/O registers will be in the first 3 bytes of the - * upper 2MB of the 4MB memory space. The board memory - * will be mapped into the low 2MB of the 4MB memory space - */ - brd->port = brd->membase + PCI_IO_OFFSET; - brd->port_end = brd->port + PCI_IO_SIZE; - - /* - * Special initialization for non-PLX boards - */ - if (brd->device != PCI_DEV_XRJ_DID && brd->device != PCI_DEV_EPCJ_DID) { - unsigned short cmd; - - pci_write_config_byte(pdev, 0x40, 0); - pci_write_config_byte(pdev, 0x46, 0); - - /* Limit burst length to 2 doubleword transactions */ - pci_write_config_byte(pdev, 0x42, 1); +static char *dgap_create_config_string(struct board_t *bd, char *string) +{ + char *ptr = string; + struct cnode *p; + struct cnode *q; + int speed; - /* - * Enable IO and mem if not already done. - * This was needed for support on Itanium. - */ - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); - pci_write_config_word(pdev, PCI_COMMAND, cmd); + if (!bd) { + *ptr = 0xff; + return string; } - /* init our poll helper tasklet */ - tasklet_init(&brd->helper_tasklet, dgap_poll_tasklet, - (unsigned long) brd); - - ret = dgap_remap(brd); - if (ret) - goto free_brd; - - pr_info("dgap: board %d: %s (rev %d), irq %ld\n", - boardnum, brd->name, brd->rev, brd->irq); + for (p = bd->bd_config; p; p = p->next) { - return brd; + switch (p->type) { + case LNODE: + *ptr = '\0'; + ptr++; + *ptr = p->u.line.speed; + ptr++; + break; + case CNODE: + /* + * Because the EPC/con concentrators can have EM modules + * hanging off of them, we have to walk ahead in the + * list and keep adding the number of ports on each EM + * to the config. UGH! + */ + speed = p->u.conc.speed; + q = p->next; + if (q && (q->type == MNODE)) { + *ptr = (p->u.conc.nport + 0x80); + ptr++; + p = q; + while (q->next && (q->next->type) == MNODE) { + *ptr = (q->u.module.nport + 0x80); + ptr++; + p = q; + q = q->next; + } + *ptr = q->u.module.nport; + ptr++; + } else { + *ptr = p->u.conc.nport; + ptr++; + } -free_brd: - kfree(brd); + *ptr = speed; + ptr++; + break; + } + } - return ERR_PTR(ret); + *ptr = 0xff; + return string; } - -static int dgap_request_irq(struct board_t *brd) +/* + * Parse a configuration file read into memory as a string. + */ +static int dgap_parsefile(char **in) { + struct cnode *p, *brd, *line, *conc; int rc; + char *s; + int linecnt = 0; - if (!brd || brd->magic != DGAP_BOARD_MAGIC) - return -ENODEV; - - /* - * Set up our interrupt handler if we are set to do interrupts. - */ - if (dgap_config_get_useintr(brd) && brd->irq) { + p = &dgap_head; + brd = line = conc = NULL; - rc = request_irq(brd->irq, dgap_intr, IRQF_SHARED, "DGAP", brd); + /* perhaps we are adding to an existing list? */ + while (p->next) + p = p->next; - if (!rc) - brd->intr_used = 1; + /* file must start with a BEGIN */ + while ((rc = dgap_gettok(in)) != BEGIN) { + if (rc == 0) { + pr_err("unexpected EOF"); + return -1; + } } - return 0; -} -static void dgap_free_irq(struct board_t *brd) -{ - if (brd->intr_used && brd->irq) - free_irq(brd->irq, brd); -} + for (; ;) { + int board_type = 0; + int conc_type = 0; + int module_type = 0; -static int dgap_firmware_load(struct pci_dev *pdev, int card_type, - struct board_t *brd) -{ - const struct firmware *fw; - char *tmp_ptr; - int ret; - char *dgap_config_buf; + rc = dgap_gettok(in); + if (rc == 0) { + pr_err("unexpected EOF"); + return -1; + } - dgap_get_vpd(brd); - dgap_do_reset_board(brd); + switch (rc) { + case BEGIN: /* should only be 1 begin */ + pr_err("unexpected config_begin\n"); + return -1; - if (fw_info[card_type].conf_name) { - ret = request_firmware(&fw, fw_info[card_type].conf_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "config file %s not found\n", - fw_info[card_type].conf_name); - return ret; - } + case END: + return 0; - dgap_config_buf = kzalloc(fw->size + 1, GFP_KERNEL); - if (!dgap_config_buf) { - release_firmware(fw); - return -ENOMEM; - } + case BOARD: /* board info */ + if (dgap_checknode(p)) + return -1; - memcpy(dgap_config_buf, fw->data, fw->size); - release_firmware(fw); + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* - * preserve dgap_config_buf - * as dgap_parsefile would - * otherwise alter it. - */ - tmp_ptr = dgap_config_buf; + p = p->next; - if (dgap_parsefile(&tmp_ptr) != 0) { - kfree(dgap_config_buf); - return -EINVAL; - } - kfree(dgap_config_buf); - } + p->type = BNODE; + p->u.board.status = kstrdup("No", GFP_KERNEL); + line = conc = NULL; + brd = p; + linecnt = -1; - /* - * Match this board to a config the user created for us. - */ - brd->bd_config = - dgap_find_config(brd->type, brd->pci_bus, brd->pci_slot); + board_type = dgap_gettok(in); + if (board_type == 0) { + pr_err("board !!type not specified"); + return -1; + } - /* - * Because the 4 port Xr products share the same PCI ID - * as the 8 port Xr products, if we receive a NULL config - * back, and this is a PAPORT8 board, retry with a - * PAPORT4 attempt as well. - */ - if (brd->type == PAPORT8 && !brd->bd_config) - brd->bd_config = - dgap_find_config(PAPORT4, brd->pci_bus, brd->pci_slot); + p->u.board.type = board_type; - if (!brd->bd_config) { - dev_err(&pdev->dev, "No valid configuration found\n"); - return -EINVAL; - } + break; - if (fw_info[card_type].bios_name) { - ret = request_firmware(&fw, fw_info[card_type].bios_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "bios file %s not found\n", - fw_info[card_type].bios_name); - return ret; - } - dgap_do_bios_load(brd, fw->data, fw->size); - release_firmware(fw); - - /* Wait for BIOS to test board... */ - ret = dgap_test_bios(brd); - if (ret) - return ret; - } - - if (fw_info[card_type].fep_name) { - ret = request_firmware(&fw, fw_info[card_type].fep_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "dgap: fep file %s not found\n", - fw_info[card_type].fep_name); - return ret; - } - dgap_do_fep_load(brd, fw->data, fw->size); - release_firmware(fw); - - /* Wait for FEP to load on board... */ - ret = dgap_test_fep(brd); - if (ret) - return ret; - } - -#ifdef DIGI_CONCENTRATORS_SUPPORTED - /* - * If this is a CX or EPCX, we need to see if the firmware - * is requesting a concentrator image from us. - */ - if ((bd->type == PCX) || (bd->type == PEPC)) { - chk_addr = (u16 *) (vaddr + DOWNREQ); - /* Nonzero if FEP is requesting concentrator image. */ - check = readw(chk_addr); - vaddr = brd->re_map_membase; - } - - if (fw_info[card_type].con_name && check && vaddr) { - ret = request_firmware(&fw, fw_info[card_type].con_name, - &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "conc file %s not found\n", - fw_info[card_type].con_name); - return ret; - } - /* Put concentrator firmware loading code here */ - offset = readw((u16 *) (vaddr + DOWNREQ)); - memcpy_toio(offset, fw->data, fw->size); - - dgap_do_conc_load(brd, (char *)fw->data, fw->size) - release_firmware(fw); - } -#endif - - return 0; -} - -/* - * Remap PCI memory. - */ -static int dgap_remap(struct board_t *brd) -{ - if (!brd || brd->magic != DGAP_BOARD_MAGIC) - return -EIO; - - if (!request_mem_region(brd->membase, 0x200000, "dgap")) - return -ENOMEM; - - if (!request_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000, - "dgap")) { - release_mem_region(brd->membase, 0x200000); - return -ENOMEM; - } - - brd->re_map_membase = ioremap(brd->membase, 0x200000); - if (!brd->re_map_membase) { - release_mem_region(brd->membase, 0x200000); - release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); - return -ENOMEM; - } - - brd->re_map_port = ioremap((brd->membase + PCI_IO_OFFSET), 0x200000); - if (!brd->re_map_port) { - release_mem_region(brd->membase, 0x200000); - release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); - iounmap(brd->re_map_membase); - return -ENOMEM; - } - - return 0; -} - -static void dgap_unmap(struct board_t *brd) -{ - iounmap(brd->re_map_port); - iounmap(brd->re_map_membase); - release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); - release_mem_region(brd->membase, 0x200000); -} -/***************************************************************************** -* -* Function: -* -* dgap_poll_handler -* -* Author: -* -* Scott H Kilau -* -* Parameters: -* -* dummy -- ignored -* -* Return Values: -* -* none -* -* Description: -* -* As each timer expires, it determines (a) whether the "transmit" -* waiter needs to be woken up, and (b) whether the poller needs to -* be rescheduled. -* -******************************************************************************/ - -static void dgap_poll_handler(ulong dummy) -{ - unsigned int i; - struct board_t *brd; - unsigned long lock_flags; - ulong new_time; - - dgap_poll_counter++; - - /* - * Do not start the board state machine until - * driver tells us its up and running, and has - * everything it needs. - */ - if (dgap_driver_state != DRIVER_READY) - goto schedule_poller; - - /* - * If we have just 1 board, or the system is not SMP, - * then use the typical old style poller. - * Otherwise, use our new tasklet based poller, which should - * speed things up for multiple boards. - */ - if ((dgap_numboards == 1) || (num_online_cpus() <= 1)) { - for (i = 0; i < dgap_numboards; i++) { - - brd = dgap_board[i]; - - if (brd->state == BOARD_FAILED) - continue; - if (!brd->intr_running) - /* Call the real board poller directly */ - dgap_poll_tasklet((unsigned long) brd); - } - } else { - /* - * Go thru each board, kicking off a - * tasklet for each if needed - */ - for (i = 0; i < dgap_numboards; i++) { - brd = dgap_board[i]; - - /* - * Attempt to grab the board lock. - * - * If we can't get it, no big deal, the next poll - * will get it. Basically, I just really don't want - * to spin in here, because I want to kick off my - * tasklets as fast as I can, and then get out the - * poller. - */ - if (!spin_trylock(&brd->bd_lock)) - continue; + case IO: /* i/o port */ + if (p->type != BNODE) { + pr_err("IO port only valid for boards"); + return -1; + } + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.board.portstr = kstrdup(s, GFP_KERNEL); + if (kstrtol(s, 0, &p->u.board.port)) { + pr_err("bad number for IO port"); + return -1; + } + p->u.board.v_port = 1; + break; - /* - * If board is in a failed state, don't bother - * scheduling a tasklet - */ - if (brd->state == BOARD_FAILED) { - spin_unlock(&brd->bd_lock); - continue; + case MEM: /* memory address */ + if (p->type != BNODE) { + pr_err("memory address only valid for boards"); + return -1; + } + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.board.addrstr = kstrdup(s, GFP_KERNEL); + if (kstrtoul(s, 0, &p->u.board.addr)) { + pr_err("bad number for memory address"); + return -1; } + p->u.board.v_addr = 1; + break; - /* Schedule a poll helper task */ - if (!brd->intr_running) - tasklet_schedule(&brd->helper_tasklet); + case PCIINFO: /* pci information */ + if (p->type != BNODE) { + pr_err("memory address only valid for boards"); + return -1; + } + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.board.pcibusstr = kstrdup(s, GFP_KERNEL); + if (kstrtoul(s, 0, &p->u.board.pcibus)) { + pr_err("bad number for pci bus"); + return -1; + } + p->u.board.v_pcibus = 1; + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.board.pcislotstr = kstrdup(s, GFP_KERNEL); + if (kstrtoul(s, 0, &p->u.board.pcislot)) { + pr_err("bad number for pci slot"); + return -1; + } + p->u.board.v_pcislot = 1; + break; - /* - * Can't do DGAP_UNLOCK here, as we don't have - * lock_flags because we did a trylock above. - */ - spin_unlock(&brd->bd_lock); - } - } - -schedule_poller: - - /* - * Schedule ourself back at the nominal wakeup interval. - */ - spin_lock_irqsave(&dgap_poll_lock, lock_flags); - dgap_poll_time += dgap_jiffies_from_ms(dgap_poll_tick); - - new_time = dgap_poll_time - jiffies; - - if ((ulong) new_time >= 2 * dgap_poll_tick) { - dgap_poll_time = - jiffies + dgap_jiffies_from_ms(dgap_poll_tick); - } - - dgap_poll_timer.function = dgap_poll_handler; - dgap_poll_timer.data = 0; - dgap_poll_timer.expires = dgap_poll_time; - spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - - if (!dgap_poll_stop) - add_timer(&dgap_poll_timer); -} - -/* - * dgap_intr() - * - * Driver interrupt handler. - */ -static irqreturn_t dgap_intr(int irq, void *voidbrd) -{ - struct board_t *brd = voidbrd; - - if (!brd) - return IRQ_NONE; - - /* - * Check to make sure its for us. - */ - if (brd->magic != DGAP_BOARD_MAGIC) - return IRQ_NONE; - - brd->intr_count++; - - /* - * Schedule tasklet to run at a better time. - */ - tasklet_schedule(&brd->helper_tasklet); - return IRQ_HANDLED; -} - -/* - * dgap_init_globals() - * - * This is where we initialize the globals from the static insmod - * configuration variables. These are declared near the head of - * this file. - */ -static void dgap_init_globals(void) -{ - unsigned int i; - - for (i = 0; i < MAXBOARDS; i++) - dgap_board[i] = NULL; - - init_timer(&dgap_poll_timer); -} - -/************************************************************************ - * - * TTY Initialization/Cleanup Functions - * - ************************************************************************/ - -/* - * dgap_tty_register() - * - * Init the tty subsystem for this board. - */ -static int dgap_tty_register(struct board_t *brd) -{ - int rc; - - brd->serial_driver = tty_alloc_driver(MAXPORTS, 0); - if (IS_ERR(brd->serial_driver)) - return PTR_ERR(brd->serial_driver); - - snprintf(brd->serial_name, MAXTTYNAMELEN, "tty_dgap_%d_", - brd->boardnum); - brd->serial_driver->name = brd->serial_name; - brd->serial_driver->name_base = 0; - brd->serial_driver->major = 0; - brd->serial_driver->minor_start = 0; - brd->serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - brd->serial_driver->subtype = SERIAL_TYPE_NORMAL; - brd->serial_driver->init_termios = dgap_default_termios; - brd->serial_driver->driver_name = DRVSTR; - brd->serial_driver->flags = (TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - - /* The kernel wants space to store pointers to tty_structs */ - brd->serial_driver->ttys = - kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); - if (!brd->serial_driver->ttys) { - rc = -ENOMEM; - goto free_serial_drv; - } - - /* - * Entry points for driver. Called by the kernel from - * tty_io.c and n_tty.c. - */ - tty_set_operations(brd->serial_driver, &dgap_tty_ops); - - /* - * If we're doing transparent print, we have to do all of the above - * again, separately so we don't get the LD confused about what major - * we are when we get into the dgap_tty_open() routine. - */ - brd->print_driver = tty_alloc_driver(MAXPORTS, 0); - if (IS_ERR(brd->print_driver)) { - rc = PTR_ERR(brd->print_driver); - goto free_serial_drv; - } - - snprintf(brd->print_name, MAXTTYNAMELEN, "pr_dgap_%d_", - brd->boardnum); - brd->print_driver->name = brd->print_name; - brd->print_driver->name_base = 0; - brd->print_driver->major = 0; - brd->print_driver->minor_start = 0; - brd->print_driver->type = TTY_DRIVER_TYPE_SERIAL; - brd->print_driver->subtype = SERIAL_TYPE_NORMAL; - brd->print_driver->init_termios = dgap_default_termios; - brd->print_driver->driver_name = DRVSTR; - brd->print_driver->flags = (TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - - /* The kernel wants space to store pointers to tty_structs */ - brd->print_driver->ttys = - kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); - if (!brd->print_driver->ttys) { - rc = -ENOMEM; - goto free_print_drv; - } - - /* - * Entry points for driver. Called by the kernel from - * tty_io.c and n_tty.c. - */ - tty_set_operations(brd->print_driver, &dgap_tty_ops); - - /* Register tty devices */ - rc = tty_register_driver(brd->serial_driver); - if (rc < 0) - goto free_print_drv; - - /* Register Transparent Print devices */ - rc = tty_register_driver(brd->print_driver); - if (rc < 0) - goto unregister_serial_drv; - - dgap_boards_by_major[brd->serial_driver->major] = brd; - brd->dgap_serial_major = brd->serial_driver->major; - - dgap_boards_by_major[brd->print_driver->major] = brd; - brd->dgap_transparent_print_major = brd->print_driver->major; - - return 0; - -unregister_serial_drv: - tty_unregister_driver(brd->serial_driver); -free_print_drv: - put_tty_driver(brd->print_driver); -free_serial_drv: - put_tty_driver(brd->serial_driver); - - return rc; -} - -static void dgap_tty_unregister(struct board_t *brd) -{ - tty_unregister_driver(brd->print_driver); - tty_unregister_driver(brd->serial_driver); - put_tty_driver(brd->print_driver); - put_tty_driver(brd->serial_driver); -} - -/* - * dgap_tty_init() - * - * Init the tty subsystem. Called once per board after board has been - * downloaded and init'ed. - */ -static int dgap_tty_init(struct board_t *brd) -{ - int i; - int tlw; - uint true_count; - u8 __iomem *vaddr; - u8 modem; - struct channel_t *ch; - struct bs_t __iomem *bs; - struct cm_t __iomem *cm; - int ret; - - /* - * Initialize board structure elements. - */ - - vaddr = brd->re_map_membase; - true_count = readw((vaddr + NCHAN)); - - brd->nasync = dgap_config_get_num_prts(brd); - - if (!brd->nasync) - brd->nasync = brd->maxports; - - if (brd->nasync > brd->maxports) - brd->nasync = brd->maxports; - - if (true_count != brd->nasync) { - dev_warn(&brd->pdev->dev, - "%s configured for %d ports, has %d ports.\n", - brd->name, brd->nasync, true_count); - - if ((brd->type == PPCM) && - (true_count == 64 || true_count == 0)) { - dev_warn(&brd->pdev->dev, - "Please make SURE the EBI cable running from the card\n"); - dev_warn(&brd->pdev->dev, - "to each EM module is plugged into EBI IN!\n"); - } - - brd->nasync = true_count; - - /* If no ports, don't bother going any further */ - if (!brd->nasync) { - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; - return -EIO; - } - } - - /* - * Allocate channel memory that might not have been allocated - * when the driver was first loaded. - */ - for (i = 0; i < brd->nasync; i++) { - brd->channels[i] = - kzalloc(sizeof(struct channel_t), GFP_KERNEL); - if (!brd->channels[i]) { - ret = -ENOMEM; - goto free_chan; - } - } - - ch = brd->channels[0]; - vaddr = brd->re_map_membase; - - bs = (struct bs_t __iomem *) ((ulong) vaddr + CHANBUF); - cm = (struct cm_t __iomem *) ((ulong) vaddr + CMDBUF); - - brd->bd_bs = bs; - - /* Set up channel variables */ - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) { - - spin_lock_init(&ch->ch_lock); - - /* Store all our magic numbers */ - ch->magic = DGAP_CHANNEL_MAGIC; - ch->ch_tun.magic = DGAP_UNIT_MAGIC; - ch->ch_tun.un_type = DGAP_SERIAL; - ch->ch_tun.un_ch = ch; - ch->ch_tun.un_dev = i; - - ch->ch_pun.magic = DGAP_UNIT_MAGIC; - ch->ch_pun.un_type = DGAP_PRINT; - ch->ch_pun.un_ch = ch; - ch->ch_pun.un_dev = i; - - ch->ch_vaddr = vaddr; - ch->ch_bs = bs; - ch->ch_cm = cm; - ch->ch_bd = brd; - ch->ch_portnum = i; - ch->ch_digi = dgap_digi_init; - - /* - * Set up digi dsr and dcd bits based on altpin flag. - */ - if (dgap_config_get_altpin(brd)) { - ch->ch_dsr = DM_CD; - ch->ch_cd = DM_DSR; - ch->ch_digi.digi_flags |= DIGI_ALTPIN; - } else { - ch->ch_cd = DM_CD; - ch->ch_dsr = DM_DSR; - } - - ch->ch_taddr = vaddr + (ioread16(&(ch->ch_bs->tx_seg)) << 4); - ch->ch_raddr = vaddr + (ioread16(&(ch->ch_bs->rx_seg)) << 4); - ch->ch_tx_win = 0; - ch->ch_rx_win = 0; - ch->ch_tsize = readw(&(ch->ch_bs->tx_max)) + 1; - ch->ch_rsize = readw(&(ch->ch_bs->rx_max)) + 1; - ch->ch_tstart = 0; - ch->ch_rstart = 0; - - /* - * Set queue water marks, interrupt mask, - * and general tty parameters. - */ - tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : - ch->ch_tsize / 2; - ch->ch_tlw = tlw; - - dgap_cmdw(ch, STLOW, tlw, 0); - - dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0); - - dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0); - - ch->ch_mistat = readb(&(ch->ch_bs->m_stat)); - - init_waitqueue_head(&ch->ch_flags_wait); - init_waitqueue_head(&ch->ch_tun.un_flags_wait); - init_waitqueue_head(&ch->ch_pun.un_flags_wait); - - /* Turn on all modem interrupts for now */ - modem = (DM_CD | DM_DSR | DM_CTS | DM_RI); - writeb(modem, &(ch->ch_bs->m_int)); - - /* - * Set edelay to 0 if interrupts are turned on, - * otherwise set edelay to the usual 100. - */ - if (brd->intr_used) - writew(0, &(ch->ch_bs->edelay)); - else - writew(100, &(ch->ch_bs->edelay)); - - writeb(1, &(ch->ch_bs->idata)); - } - - return 0; - -free_chan: - while (--i >= 0) { - kfree(brd->channels[i]); - brd->channels[i] = NULL; - } - return ret; -} - -/* - * dgap_tty_free() - * - * Free the channles which are allocated in dgap_tty_init(). - */ -static void dgap_tty_free(struct board_t *brd) -{ - int i; - - for (i = 0; i < brd->nasync; i++) - kfree(brd->channels[i]); -} -/* - * dgap_cleanup_tty() - * - * Uninitialize the TTY portion of this driver. Free all memory and - * resources. - */ -static void dgap_cleanup_tty(struct board_t *brd) -{ - struct device *dev; - unsigned int i; - - dgap_boards_by_major[brd->serial_driver->major] = NULL; - brd->dgap_serial_major = 0; - for (i = 0; i < brd->nasync; i++) { - tty_port_destroy(&brd->serial_ports[i]); - dev = brd->channels[i]->ch_tun.un_sysfs; - dgap_remove_tty_sysfs(dev); - tty_unregister_device(brd->serial_driver, i); - } - tty_unregister_driver(brd->serial_driver); - put_tty_driver(brd->serial_driver); - kfree(brd->serial_ports); - - dgap_boards_by_major[brd->print_driver->major] = NULL; - brd->dgap_transparent_print_major = 0; - for (i = 0; i < brd->nasync; i++) { - tty_port_destroy(&brd->printer_ports[i]); - dev = brd->channels[i]->ch_pun.un_sysfs; - dgap_remove_tty_sysfs(dev); - tty_unregister_device(brd->print_driver, i); - } - tty_unregister_driver(brd->print_driver); - put_tty_driver(brd->print_driver); - kfree(brd->printer_ports); -} - -/*======================================================================= - * - * dgap_input - Process received data. - * - * ch - Pointer to channel structure. - * - *=======================================================================*/ - -static void dgap_input(struct channel_t *ch) -{ - struct board_t *bd; - struct bs_t __iomem *bs; - struct tty_struct *tp; - struct tty_ldisc *ld; - uint rmask; - uint head; - uint tail; - int data_len; - ulong lock_flags; - ulong lock_flags2; - int flip_len; - int len; - int n; - u8 *buf; - u8 tmpchar; - int s; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - tp = ch->ch_tun.un_tty; - - bs = ch->ch_bs; - if (!bs) - return; - - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + case METHOD: + if (p->type != BNODE) { + pr_err("install method only valid for boards"); + return -1; + } + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.board.method = kstrdup(s, GFP_KERNEL); + p->u.board.v_method = 1; + break; - /* - * Figure the number of characters in the buffer. - * Exit immediately if none. - */ + case STATUS: + if (p->type != BNODE) { + pr_err("config status only valid for boards"); + return -1; + } + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.board.status = kstrdup(s, GFP_KERNEL); + break; - rmask = ch->ch_rsize - 1; + case NPORTS: /* number of ports */ + if (p->type == BNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.board.nport)) { + pr_err("bad number for number of ports"); + return -1; + } + p->u.board.v_nport = 1; + } else if (p->type == CNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.conc.nport)) { + pr_err("bad number for number of ports"); + return -1; + } + p->u.conc.v_nport = 1; + } else if (p->type == MNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.module.nport)) { + pr_err("bad number for number of ports"); + return -1; + } + p->u.module.v_nport = 1; + } else { + pr_err("nports only valid for concentrators or modules"); + return -1; + } + break; - head = readw(&(bs->rx_head)); - head &= rmask; - tail = readw(&(bs->rx_tail)); - tail &= rmask; + case ID: /* letter ID used in tty name */ + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } - data_len = (head - tail) & rmask; + p->u.board.status = kstrdup(s, GFP_KERNEL); - if (data_len == 0) { - writeb(1, &(bs->idata)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } + if (p->type == CNODE) { + p->u.conc.id = kstrdup(s, GFP_KERNEL); + p->u.conc.v_id = 1; + } else if (p->type == MNODE) { + p->u.module.id = kstrdup(s, GFP_KERNEL); + p->u.module.v_id = 1; + } else { + pr_err("id only valid for concentrators or modules"); + return -1; + } + break; - /* - * If the device is not open, or CREAD is off, flush - * input data and return immediately. - */ - if ((bd->state != BOARD_READY) || !tp || - (tp->magic != TTY_MAGIC) || - !(ch->ch_tun.un_flags & UN_ISOPEN) || - !(tp->termios.c_cflag & CREAD) || - (ch->ch_tun.un_flags & UN_CLOSING)) { + case STARTO: /* start offset of ID */ + if (p->type == BNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.board.start)) { + pr_err("bad number for start of tty count"); + return -1; + } + p->u.board.v_start = 1; + } else if (p->type == CNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.conc.start)) { + pr_err("bad number for start of tty count"); + return -1; + } + p->u.conc.v_start = 1; + } else if (p->type == MNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.module.start)) { + pr_err("bad number for start of tty count"); + return -1; + } + p->u.module.v_start = 1; + } else { + pr_err("start only valid for concentrators or modules"); + return -1; + } + break; - writew(head, &(bs->rx_tail)); - writeb(1, &(bs->idata)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } + case TTYN: /* tty name prefix */ + if (dgap_checknode(p)) + return -1; - /* - * If we are throttled, simply don't read any data. - */ - if (ch->ch_flags & CH_RXBLOCK) { - writeb(1, &(bs->idata)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* - * Ignore oruns. - */ - tmpchar = readb(&(bs->orun)); - if (tmpchar) { - ch->ch_err_overrun++; - writeb(0, &(bs->orun)); - } + p = p->next; + p->type = TNODE; - /* Decide how much data we can send into the tty layer */ - flip_len = TTY_FLIPBUF_SIZE; + s = dgap_getword(in); + if (!s) { + pr_err("unexpeced end of file"); + return -1; + } + p->u.ttyname = kstrdup(s, GFP_KERNEL); + if (!p->u.ttyname) + return -1; - /* Chop down the length, if needed */ - len = min(data_len, flip_len); - len = min(len, (N_TTY_BUF_SIZE - 1)); + break; - ld = tty_ldisc_ref(tp); + case CU: /* cu name prefix */ + if (dgap_checknode(p)) + return -1; -#ifdef TTY_DONT_FLIP - /* - * If the DONT_FLIP flag is on, don't flush our buffer, and act - * like the ld doesn't have any space to put the data right now. - */ - if (test_bit(TTY_DONT_FLIP, &tp->flags)) - len = 0; -#endif + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* - * If we were unable to get a reference to the ld, - * don't flush our buffer, and act like the ld doesn't - * have any space to put the data right now. - */ - if (!ld) { - len = 0; - } else { - /* - * If ld doesn't have a pointer to a receive_buf function, - * flush the data, then act like the ld doesn't have any - * space to put the data right now. - */ - if (!ld->ops->receive_buf) { - writew(head, &(bs->rx_tail)); - len = 0; - } - } + p = p->next; + p->type = CUNODE; - if (len <= 0) { - writeb(1, &(bs->idata)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (ld) - tty_ldisc_deref(ld); - return; - } + s = dgap_getword(in); + if (!s) { + pr_err("unexpeced end of file"); + return -1; + } + p->u.cuname = kstrdup(s, GFP_KERNEL); + if (!p->u.cuname) + return -1; - buf = ch->ch_bd->flipbuf; - n = len; + break; - /* - * n now contains the most amount of data we can copy, - * bounded either by our buffer size or the amount - * of data the card actually has pending... - */ - while (n) { + case LINE: /* line information */ + if (dgap_checknode(p)) + return -1; + if (!brd) { + pr_err("must specify board before line info"); + return -1; + } + switch (brd->u.board.type) { + case PPCM: + pr_err("line not valid for PC/em"); + return -1; + } - s = ((head >= tail) ? head : ch->ch_rsize) - tail; - s = min(s, n); + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - if (s <= 0) + p = p->next; + p->type = LNODE; + conc = NULL; + line = p; + linecnt++; break; - memcpy_fromio(buf, ch->ch_raddr + tail, s); - - tail += s; - buf += s; + case CONC: /* concentrator information */ + if (dgap_checknode(p)) + return -1; + if (!line) { + pr_err("must specify line info before concentrator"); + return -1; + } - n -= s; - /* Flip queue if needed */ - tail &= rmask; - } + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - writew(tail, &(bs->rx_tail)); - writeb(1, &(bs->idata)); - ch->ch_rxcount += len; + p = p->next; + p->type = CNODE; + conc = p; - /* - * If we are completely raw, we don't need to go through a lot - * of the tty layers that exist. - * In this case, we take the shortest and fastest route we - * can to relay the data to the user. - * - * On the other hand, if we are not raw, we need to go through - * the tty layer, which has its API more well defined. - */ - if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { - dgap_parity_scan(ch, ch->ch_bd->flipbuf, - ch->ch_bd->flipflagbuf, &len); + if (linecnt) + brd->u.board.conc2++; + else + brd->u.board.conc1++; - len = tty_buffer_request_room(tp->port, len); - tty_insert_flip_string_flags(tp->port, ch->ch_bd->flipbuf, - ch->ch_bd->flipflagbuf, len); - } else { - len = tty_buffer_request_room(tp->port, len); - tty_insert_flip_string(tp->port, ch->ch_bd->flipbuf, len); - } + conc_type = dgap_gettok(in); + if (conc_type == 0 || conc_type != CX || + conc_type != EPC) { + pr_err("failed to set a type of concentratros"); + return -1; + } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + p->u.conc.type = conc_type; - /* Tell the tty layer its okay to "eat" the data now */ - tty_flip_buffer_push(tp->port); + break; - if (ld) - tty_ldisc_deref(ld); + case MOD: /* EBI module */ + if (dgap_checknode(p)) + return -1; + if (!brd) { + pr_err("must specify board info before EBI modules"); + return -1; + } + switch (brd->u.board.type) { + case PPCM: + linecnt = 0; + break; + default: + if (!conc) { + pr_err("must specify concentrator info before EBI module"); + return -1; + } + } -} + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; -/************************************************************************ - * Determines when CARRIER changes state and takes appropriate - * action. - ************************************************************************/ -static void dgap_carrier(struct channel_t *ch) -{ - struct board_t *bd; + p = p->next; + p->type = MNODE; - int virt_carrier = 0; - int phys_carrier = 0; + if (linecnt) + brd->u.board.module2++; + else + brd->u.board.module1++; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + module_type = dgap_gettok(in); + if (module_type == 0 || module_type != PORTS || + module_type != MODEM) { + pr_err("failed to set a type of module"); + return -1; + } - bd = ch->ch_bd; + p->u.module.type = module_type; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; + break; - /* Make sure altpin is always set correctly */ - if (ch->ch_digi.digi_flags & DIGI_ALTPIN) { - ch->ch_dsr = DM_CD; - ch->ch_cd = DM_DSR; - } else { - ch->ch_dsr = DM_DSR; - ch->ch_cd = DM_CD; - } + case CABLE: + if (p->type == LNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.line.cable = kstrdup(s, GFP_KERNEL); + p->u.line.v_cable = 1; + } + break; - if (ch->ch_mistat & D_CD(ch)) - phys_carrier = 1; + case SPEED: /* sync line speed indication */ + if (p->type == LNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.line.speed)) { + pr_err("bad number for line speed"); + return -1; + } + p->u.line.v_speed = 1; + } else if (p->type == CNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.conc.speed)) { + pr_err("bad number for line speed"); + return -1; + } + p->u.conc.v_speed = 1; + } else { + pr_err("speed valid only for lines or concentrators."); + return -1; + } + break; - if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) - virt_carrier = 1; + case CONNECT: + if (p->type == CNODE) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + p->u.conc.connect = kstrdup(s, GFP_KERNEL); + p->u.conc.v_connect = 1; + } + break; + case PRINT: /* transparent print name prefix */ + if (dgap_checknode(p)) + return -1; - if (ch->ch_c_cflag & CLOCAL) - virt_carrier = 1; + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* - * Test for a VIRTUAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { + p = p->next; + p->type = PNODE; - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ + s = dgap_getword(in); + if (!s) { + pr_err("unexpeced end of file"); + return -1; + } + p->u.printname = kstrdup(s, GFP_KERNEL); + if (!p->u.printname) + return -1; - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } + break; - /* - * Test for a PHYSICAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { + case CMAJOR: /* major number */ + if (dgap_checknode(p)) + return -1; - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } + p = p->next; + p->type = JNODE; - /* - * Test for a PHYSICAL transition to low, so long as we aren't - * currently ignoring physical transitions (which is what "virtual - * carrier" indicates). - * - * The transition of the virtual carrier to low really doesn't - * matter... it really only means "ignore carrier state", not - * "make pretend that carrier is there". - */ - if ((virt_carrier == 0) && - ((ch->ch_flags & CH_CD) != 0) && - (phys_carrier == 0)) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.majornumber)) { + pr_err("bad number for major number"); + return -1; + } + break; - /* - * When carrier drops: - * - * Drop carrier on all open units. - * - * Flush queues, waking up any task waiting in the - * line discipline. - * - * Send a hangup to the control terminal. - * - * Enable all select calls. - */ - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); + case ALTPIN: /* altpin setting */ + if (dgap_checknode(p)) + return -1; - if (ch->ch_tun.un_open_count > 0) - tty_hangup(ch->ch_tun.un_tty); + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - if (ch->ch_pun.un_open_count > 0) - tty_hangup(ch->ch_pun.un_tty); - } + p = p->next; + p->type = ANODE; - /* - * Make sure that our cached values reflect the current reality. - */ - if (virt_carrier == 1) - ch->ch_flags |= CH_FCAR; - else - ch->ch_flags &= ~CH_FCAR; + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.altpin)) { + pr_err("bad number for altpin"); + return -1; + } + break; - if (phys_carrier == 1) - ch->ch_flags |= CH_CD; - else - ch->ch_flags &= ~CH_CD; -} + case USEINTR: /* enable interrupt setting */ + if (dgap_checknode(p)) + return -1; -/************************************************************************ - * - * TTY Entry points and helper functions - * - ************************************************************************/ + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; -/* - * dgap_tty_open() - * - */ -static int dgap_tty_open(struct tty_struct *tty, struct file *file) -{ - struct board_t *brd; - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - uint major; - uint minor; - int rc; - ulong lock_flags; - ulong lock_flags2; - u16 head; + p = p->next; + p->type = INTRNODE; + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.useintr)) { + pr_err("bad number for useintr"); + return -1; + } + break; - major = MAJOR(tty_devnum(tty)); - minor = MINOR(tty_devnum(tty)); + case TTSIZ: /* size of tty structure */ + if (dgap_checknode(p)) + return -1; - if (major > 255) - return -EIO; + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* Get board pointer from our array of majors we have allocated */ - brd = dgap_boards_by_major[major]; - if (!brd) - return -EIO; + p = p->next; + p->type = TSNODE; - /* - * If board is not yet up to a state of READY, go to - * sleep waiting for it to happen or they cancel the open. - */ - rc = wait_event_interruptible(brd->state_wait, - (brd->state & BOARD_READY)); + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.ttysize)) { + pr_err("bad number for ttysize"); + return -1; + } + break; - if (rc) - return rc; + case CHSIZ: /* channel structure size */ + if (dgap_checknode(p)) + return -1; - spin_lock_irqsave(&brd->bd_lock, lock_flags); + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* The wait above should guarantee this cannot happen */ - if (brd->state != BOARD_READY) { - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } + p = p->next; + p->type = CSNODE; - /* If opened device is greater than our number of ports, bail. */ - if (MINOR(tty_devnum(tty)) > brd->nasync) { - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.chsize)) { + pr_err("bad number for chsize"); + return -1; + } + break; - ch = brd->channels[minor]; - if (!ch) { - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } + case BSSIZ: /* board structure size */ + if (dgap_checknode(p)) + return -1; - /* Grab channel lock */ - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* Figure out our type */ - if (major == brd->dgap_serial_major) { - un = &brd->channels[minor]->ch_tun; - un->un_type = DGAP_SERIAL; - } else if (major == brd->dgap_transparent_print_major) { - un = &brd->channels[minor]->ch_pun; - un->un_type = DGAP_PRINT; - } else { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } + p = p->next; + p->type = BSNODE; - /* Store our unit into driver_data, so we always have it available. */ - tty->driver_data = un; + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.bssize)) { + pr_err("bad number for bssize"); + return -1; + } + break; - /* - * Error if channel info pointer is NULL. - */ - bs = ch->ch_bs; - if (!bs) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - return -EIO; - } + case UNTSIZ: /* sched structure size */ + if (dgap_checknode(p)) + return -1; - /* - * Initialize tty's - */ - if (!(un->un_flags & UN_ISOPEN)) { - /* Store important variables. */ - un->un_tty = tty; + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* Maybe do something here to the TTY struct as well? */ - } + p = p->next; + p->type = USNODE; - /* - * Initialize if neither terminal or printer is open. - */ - if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.unsize)) { + pr_err("bad number for schedsize"); + return -1; + } + break; - ch->ch_mforce = 0; - ch->ch_mval = 0; + case F2SIZ: /* f2200 structure size */ + if (dgap_checknode(p)) + return -1; - /* - * Flush input queue. - */ - head = readw(&(bs->rx_head)); - writew(head, &(bs->rx_tail)); + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; + + p = p->next; + p->type = FSNODE; + + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.f2size)) { + pr_err("bad number for f2200size"); + return -1; + } + break; - ch->ch_flags = 0; - ch->pscan_state = 0; - ch->pscan_savechar = 0; + case VPSIZ: /* vpix structure size */ + if (dgap_checknode(p)) + return -1; - ch->ch_c_cflag = tty->termios.c_cflag; - ch->ch_c_iflag = tty->termios.c_iflag; - ch->ch_c_oflag = tty->termios.c_oflag; - ch->ch_c_lflag = tty->termios.c_lflag; - ch->ch_startc = tty->termios.c_cc[VSTART]; - ch->ch_stopc = tty->termios.c_cc[VSTOP]; + p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); + if (!p->next) + return -1; - /* TODO: flush our TTY struct here? */ - } + p = p->next; + p->type = VSNODE; - dgap_carrier(ch); - /* - * Run param in case we changed anything - */ - dgap_param(ch, brd, un->un_type); + s = dgap_getword(in); + if (!s) { + pr_err("unexpected end of file"); + return -1; + } + if (kstrtol(s, 0, &p->u.vpixsize)) { + pr_err("bad number for vpixsize"); + return -1; + } + break; + } + } +} - /* - * follow protocol for opening port - */ +static void dgap_cleanup_nodes(void) +{ + struct cnode *p; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); + p = &dgap_head; - rc = dgap_block_til_ready(tty, file, ch); + while (p) { + struct cnode *tmp = p->next; - if (!un->un_tty) - return -ENODEV; + if (p->type == NULLNODE) { + p = tmp; + continue; + } - /* No going back now, increment our unit and channel counters */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - ch->ch_open_count++; - un->un_open_count++; - un->un_flags |= (UN_ISOPEN); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + switch (p->type) { + case BNODE: + kfree(p->u.board.portstr); + kfree(p->u.board.addrstr); + kfree(p->u.board.pcibusstr); + kfree(p->u.board.pcislotstr); + kfree(p->u.board.method); + break; + case CNODE: + kfree(p->u.conc.id); + kfree(p->u.conc.connect); + break; + case MNODE: + kfree(p->u.module.id); + break; + case TNODE: + kfree(p->u.ttyname); + break; + case CUNODE: + kfree(p->u.cuname); + break; + case LNODE: + kfree(p->u.line.cable); + break; + case PNODE: + kfree(p->u.printname); + break; + } - return rc; + kfree(p->u.board.status); + kfree(p); + p = tmp; + } } /* - * dgap_block_til_ready() - * - * Wait for DCD, if needed. + * Retrives the current custom baud rate from FEP memory, + * and returns it back to the user. + * Returns 0 on error. */ -static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, - struct channel_t *ch) +static uint dgap_get_custom_baud(struct channel_t *ch) { - int retval = 0; - struct un_t *un; - ulong lock_flags; - uint old_flags; - int sleep_on_un_flags; + u8 __iomem *vaddr; + ulong offset; + uint value; - if (!tty || tty->magic != TTY_MAGIC || !file || !ch || - ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; + if (!ch->ch_bd || ch->ch_bd->magic != DGAP_BOARD_MAGIC) + return 0; - spin_lock_irqsave(&ch->ch_lock, lock_flags); + if (!(ch->ch_bd->bd_flags & BD_FEP5PLUS)) + return 0; - ch->ch_wopen++; + vaddr = ch->ch_bd->re_map_membase; - /* Loop forever */ - while (1) { + if (!vaddr) + return 0; - sleep_on_un_flags = 0; + /* + * Go get from fep mem, what the fep + * believes the custom baud rate is. + */ + offset = (ioread16(vaddr + ECS_SEG) << 4) + (ch->ch_portnum * 0x28) + + LINE_SPEED; - /* - * If board has failed somehow during our sleep, - * bail with error. - */ - if (ch->ch_bd->state == BOARD_FAILED) { - retval = -EIO; - break; - } + value = readw(vaddr + offset); + return value; +} - /* If tty was hung up, break out of loop and set error. */ - if (tty_hung_up_p(file)) { - retval = -EAGAIN; - break; - } +/* + * Remap PCI memory. + */ +static int dgap_remap(struct board_t *brd) +{ + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return -EIO; - /* - * If either unit is in the middle of the fragile part of close, - * we just cannot touch the channel safely. - * Go back to sleep, knowing that when the channel can be - * touched safely, the close routine will signal the - * ch_wait_flags to wake us back up. - */ - if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & - UN_CLOSING)) { + if (!request_mem_region(brd->membase, 0x200000, "dgap")) + return -ENOMEM; - /* - * Our conditions to leave cleanly and happily: - * 1) NONBLOCKING on the tty is set. - * 2) CLOCAL is set. - * 3) DCD (fake or real) is active. - */ + if (!request_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000, + "dgap")) { + release_mem_region(brd->membase, 0x200000); + return -ENOMEM; + } - if (file->f_flags & O_NONBLOCK) - break; + brd->re_map_membase = ioremap(brd->membase, 0x200000); + if (!brd->re_map_membase) { + release_mem_region(brd->membase, 0x200000); + release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); + return -ENOMEM; + } - if (tty->flags & (1 << TTY_IO_ERROR)) - break; + brd->re_map_port = ioremap((brd->membase + PCI_IO_OFFSET), 0x200000); + if (!brd->re_map_port) { + release_mem_region(brd->membase, 0x200000); + release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); + iounmap(brd->re_map_membase); + return -ENOMEM; + } - if (ch->ch_flags & CH_CD) - break; + return 0; +} - if (ch->ch_flags & CH_FCAR) - break; - } else { - sleep_on_un_flags = 1; - } +static void dgap_unmap(struct board_t *brd) +{ + iounmap(brd->re_map_port); + iounmap(brd->re_map_membase); + release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); + release_mem_region(brd->membase, 0x200000); +} - /* - * If there is a signal pending, the user probably - * interrupted (ctrl-c) us. - * Leave loop with error set. - */ - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } +/* + * dgap_parity_scan() + * + * Convert the FEP5 way of reporting parity errors and breaks into + * the Linux line discipline way. + */ +static void dgap_parity_scan(struct channel_t *ch, unsigned char *cbuf, + unsigned char *fbuf, int *len) +{ + int l = *len; + int count = 0; + unsigned char *in, *cout, *fout; + unsigned char c; - /* - * Store the flags before we let go of channel lock - */ - if (sleep_on_un_flags) - old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags; - else - old_flags = ch->ch_flags; + in = cbuf; + cout = cbuf; + fout = fbuf; + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + while (l--) { + c = *in++; + switch (ch->pscan_state) { + default: + /* reset to sanity and fall through */ + ch->pscan_state = 0; - /* - * Let go of channel lock before calling schedule. - * Our poller will get any FEP events and wake us up when DCD - * eventually goes active. - */ + case 0: + /* No FF seen yet */ + if (c == (unsigned char) '\377') + /* delete this character from stream */ + ch->pscan_state = 1; + else { + *cout++ = c; + *fout++ = TTY_NORMAL; + count += 1; + } + break; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + case 1: + /* first FF seen */ + if (c == (unsigned char) '\377') { + /* doubled ff, transform to single ff */ + *cout++ = c; + *fout++ = TTY_NORMAL; + count += 1; + ch->pscan_state = 0; + } else { + /* save value examination in next state */ + ch->pscan_savechar = c; + ch->pscan_state = 2; + } + break; - /* - * Wait for something in the flags to change - * from the current value. - */ - if (sleep_on_un_flags) { - retval = wait_event_interruptible(un->un_flags_wait, - (old_flags != (ch->ch_tun.un_flags | - ch->ch_pun.un_flags))); - } else { - retval = wait_event_interruptible(ch->ch_flags_wait, - (old_flags != ch->ch_flags)); - } + case 2: + /* third character of ff sequence */ - /* - * We got woken up for some reason. - * Before looping around, grab our channel lock. - */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - } + *cout++ = c; - ch->ch_wopen--; + if (ch->pscan_savechar == 0x0) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + if (c == 0x0) { + ch->ch_err_break++; + *fout++ = TTY_BREAK; + } else { + ch->ch_err_parity++; + *fout++ = TTY_PARITY; + } + } - return retval; + count += 1; + ch->pscan_state = 0; + } + } + *len = count; } -/* - * dgap_tty_hangup() +/*======================================================================= * - * Hangup the port. Like a close, but don't wait for output to drain. - */ -static void dgap_tty_hangup(struct tty_struct *tty) + * dgap_input - Process received data. + * + * ch - Pointer to channel structure. + * + *=======================================================================*/ + +static void dgap_input(struct channel_t *ch) { struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + struct bs_t __iomem *bs; + struct tty_struct *tp; + struct tty_ldisc *ld; + uint rmask; + uint head; + uint tail; + int data_len; + ulong lock_flags; + ulong lock_flags2; + int flip_len; + int len; + int n; + u8 *buf; + u8 tmpchar; + int s; - if (!tty || tty->magic != TTY_MAGIC) + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) return; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; + tp = ch->ch_tun.un_tty; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + bs = ch->ch_bs; + if (!bs) return; bd = ch->ch_bd; if (!bd || bd->magic != DGAP_BOARD_MAGIC) return; - /* flush the transmit queues */ - dgap_tty_flush_buffer(tty); -} + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); -/* - * dgap_tty_close() - * - */ -static void dgap_tty_close(struct tty_struct *tty, struct file *file) -{ - struct ktermios *ts; - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; + /* + * Figure the number of characters in the buffer. + * Exit immediately if none. + */ - if (!tty || tty->magic != TTY_MAGIC) - return; + rmask = ch->ch_rsize - 1; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) + head = readw(&(bs->rx_head)); + head &= rmask; + tail = readw(&(bs->rx_tail)); + tail &= rmask; + + data_len = (head - tail) & rmask; + + if (data_len == 0) { + writeb(1, &(bs->idata)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); return; + } - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + /* + * If the device is not open, or CREAD is off, flush + * input data and return immediately. + */ + if ((bd->state != BOARD_READY) || !tp || + (tp->magic != TTY_MAGIC) || + !(ch->ch_tun.un_flags & UN_ISOPEN) || + !(tp->termios.c_cflag & CREAD) || + (ch->ch_tun.un_flags & UN_CLOSING)) { + + writew(head, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); return; + } - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) + /* + * If we are throttled, simply don't read any data. + */ + if (ch->ch_flags & CH_RXBLOCK) { + writeb(1, &(bs->idata)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); return; + } - ts = &tty->termios; + /* + * Ignore oruns. + */ + tmpchar = readb(&(bs->orun)); + if (tmpchar) { + ch->ch_err_overrun++; + writeb(0, &(bs->orun)); + } - spin_lock_irqsave(&ch->ch_lock, lock_flags); + /* Decide how much data we can send into the tty layer */ + flip_len = TTY_FLIPBUF_SIZE; + + /* Chop down the length, if needed */ + len = min(data_len, flip_len); + len = min(len, (N_TTY_BUF_SIZE - 1)); + + ld = tty_ldisc_ref(tp); +#ifdef TTY_DONT_FLIP /* - * Determine if this is the last close or not - and if we agree about - * which type of close it is with the Line Discipline + * If the DONT_FLIP flag is on, don't flush our buffer, and act + * like the ld doesn't have any space to put the data right now. */ - if ((tty->count == 1) && (un->un_open_count != 1)) { + if (test_bit(TTY_DONT_FLIP, &tp->flags)) + len = 0; +#endif + + /* + * If we were unable to get a reference to the ld, + * don't flush our buffer, and act like the ld doesn't + * have any space to put the data right now. + */ + if (!ld) { + len = 0; + } else { /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. un_open_count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. + * If ld doesn't have a pointer to a receive_buf function, + * flush the data, then act like the ld doesn't have any + * space to put the data right now. */ - un->un_open_count = 1; + if (!ld->ops->receive_buf) { + writew(head, &(bs->rx_tail)); + len = 0; + } } - if (--un->un_open_count < 0) - un->un_open_count = 0; - - ch->ch_open_count--; - - if (ch->ch_open_count && un->un_open_count) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + if (len <= 0) { + writeb(1, &(bs->idata)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (ld) + tty_ldisc_deref(ld); return; } - /* OK, its the last close on the unit */ + buf = ch->ch_bd->flipbuf; + n = len; + + /* + * n now contains the most amount of data we can copy, + * bounded either by our buffer size or the amount + * of data the card actually has pending... + */ + while (n) { + + s = ((head >= tail) ? head : ch->ch_rsize) - tail; + s = min(s, n); + + if (s <= 0) + break; + + memcpy_fromio(buf, ch->ch_raddr + tail, s); + + tail += s; + buf += s; - un->un_flags |= UN_CLOSING; + n -= s; + /* Flip queue if needed */ + tail &= rmask; + } - tty->closing = 1; + writew(tail, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + ch->ch_rxcount += len; /* - * Only officially close channel if count is 0 and - * DIGI_PRINTER bit is not set. + * If we are completely raw, we don't need to go through a lot + * of the tty layers that exist. + * In this case, we take the shortest and fastest route we + * can to relay the data to the user. + * + * On the other hand, if we are not raw, we need to go through + * the tty layer, which has its API more well defined. */ - if ((ch->ch_open_count == 0) && - !(ch->ch_digi.digi_flags & DIGI_PRINTER)) { - - ch->ch_flags &= ~(CH_RXBLOCK); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* wait for output to drain */ - /* This will also return if we take an interrupt */ + if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { + dgap_parity_scan(ch, ch->ch_bd->flipbuf, + ch->ch_bd->flipflagbuf, &len); - dgap_wait_for_drain(tty); + len = tty_buffer_request_room(tp->port, len); + tty_insert_flip_string_flags(tp->port, ch->ch_bd->flipbuf, + ch->ch_bd->flipflagbuf, len); + } else { + len = tty_buffer_request_room(tp->port, len); + tty_insert_flip_string(tp->port, ch->ch_bd->flipbuf, len); + } - dgap_tty_flush_buffer(tty); - tty_ldisc_flush(tty); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags); + /* Tell the tty layer its okay to "eat" the data now */ + tty_flip_buffer_push(tp->port); - tty->closing = 0; + if (ld) + tty_ldisc_deref(ld); - /* - * If we have HUPCL set, lower DTR and RTS - */ - if (ch->ch_c_cflag & HUPCL) { - ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch)); - dgap_cmdb(ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0); +} - /* - * Go to sleep to ensure RTS/DTR - * have been dropped for modems to see it. - */ - spin_unlock_irqrestore(&ch->ch_lock, - lock_flags); +static void dgap_write_wakeup(struct board_t *bd, struct channel_t *ch, + struct un_t *un, u32 mask, + unsigned long *irq_flags1, + unsigned long *irq_flags2) +{ + if (!(un->un_flags & mask)) + return; - /* .25 second delay for dropping RTS/DTR */ - schedule_timeout_interruptible(msecs_to_jiffies(250)); + un->un_flags &= ~mask; - spin_lock_irqsave(&ch->ch_lock, lock_flags); - } + if (!(un->un_flags & UN_ISOPEN)) + return; - ch->pscan_state = 0; - ch->pscan_savechar = 0; - ch->ch_baud_info = 0; + if ((un->un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + un->un_tty->ldisc->ops->write_wakeup) { + spin_unlock_irqrestore(&ch->ch_lock, *irq_flags2); + spin_unlock_irqrestore(&bd->bd_lock, *irq_flags1); - } + (un->un_tty->ldisc->ops->write_wakeup)(un->un_tty); - /* - * turn off print device when closing print device. - */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int) ch->ch_digi.digi_offlen); - ch->ch_flags &= ~CH_PRON; + spin_lock_irqsave(&bd->bd_lock, *irq_flags1); + spin_lock_irqsave(&ch->ch_lock, *irq_flags2); } - - un->un_tty = NULL; - un->un_flags &= ~(UN_ISOPEN | UN_CLOSING); - tty->driver_data = NULL; - - wake_up_interruptible(&ch->ch_flags_wait); + wake_up_interruptible(&un->un_tty->write_wait); wake_up_interruptible(&un->un_flags_wait); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); } -/* - * dgap_tty_chars_in_buffer() - * - * Return number of characters that have not been transmitted yet. - * - * This routine is used by the line discipline to determine if there - * is data waiting to be transmitted/drained/flushed or not. - */ -static int dgap_tty_chars_in_buffer(struct tty_struct *tty) +/************************************************************************ + * Determines when CARRIER changes state and takes appropriate + * action. + ************************************************************************/ +static void dgap_carrier(struct channel_t *ch) { struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - u8 tbusy; - uint chars; - u16 thead, ttail, tmask, chead, ctail; - ulong lock_flags = 0; - ulong lock_flags2 = 0; - - if (!tty) - return 0; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; + int virt_carrier = 0; + int phys_carrier = 0; - ch = un->un_ch; if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; + return; bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; + return; - bs = ch->ch_bs; - if (!bs) - return 0; + /* Make sure altpin is always set correctly */ + if (ch->ch_digi.digi_flags & DIGI_ALTPIN) { + ch->ch_dsr = DM_CD; + ch->ch_cd = DM_DSR; + } else { + ch->ch_dsr = DM_DSR; + ch->ch_cd = DM_CD; + } - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + if (ch->ch_mistat & D_CD(ch)) + phys_carrier = 1; - tmask = (ch->ch_tsize - 1); + if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) + virt_carrier = 1; - /* Get Transmit queue pointers */ - thead = readw(&(bs->tx_head)) & tmask; - ttail = readw(&(bs->tx_tail)) & tmask; + if (ch->ch_c_cflag & CLOCAL) + virt_carrier = 1; - /* Get tbusy flag */ - tbusy = readb(&(bs->tbusy)); + /* + * Test for a VIRTUAL carrier transition to HIGH. + */ + if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { - /* Get Command queue pointers */ - chead = readw(&(ch->ch_cm->cm_head)); - ctail = readw(&(ch->ch_cm->cm_tail)); + /* + * When carrier rises, wake any threads waiting + * for carrier in the open routine. + */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } /* - * The only way we know for sure if there is no pending - * data left to be transferred, is if: - * 1) Transmit head and tail are equal (empty). - * 2) Command queue head and tail are equal (empty). - * 3) The "TBUSY" flag is 0. (Transmitter not busy). + * Test for a PHYSICAL carrier transition to HIGH. */ + if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { - if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) { - chars = 0; - } else { - if (thead >= ttail) - chars = thead - ttail; - else - chars = thead - ttail + ch->ch_tsize; /* - * Fudge factor here. - * If chars is zero, we know that the command queue had - * something in it or tbusy was set. Because we cannot - * be sure if there is still some data to be transmitted, - * lets lie, and tell ld we have 1 byte left. + * When carrier rises, wake any threads waiting + * for carrier in the open routine. */ - if (chars == 0) { - /* - * If TBUSY is still set, and our tx buffers are empty, - * force the firmware to send me another wakeup after - * TBUSY has been cleared. - */ - if (tbusy != 0) { - spin_lock_irqsave(&ch->ch_lock, lock_flags); - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); - spin_unlock_irqrestore(&ch->ch_lock, - lock_flags); - } - chars = 1; - } + + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Test for a PHYSICAL transition to low, so long as we aren't + * currently ignoring physical transitions (which is what "virtual + * carrier" indicates). + * + * The transition of the virtual carrier to low really doesn't + * matter... it really only means "ignore carrier state", not + * "make pretend that carrier is there". + */ + if ((virt_carrier == 0) && + ((ch->ch_flags & CH_CD) != 0) && + (phys_carrier == 0)) { + + /* + * When carrier drops: + * + * Drop carrier on all open units. + * + * Flush queues, waking up any task waiting in the + * line discipline. + * + * Send a hangup to the control terminal. + * + * Enable all select calls. + */ + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + + if (ch->ch_tun.un_open_count > 0) + tty_hangup(ch->ch_tun.un_tty); + + if (ch->ch_pun.un_open_count > 0) + tty_hangup(ch->ch_pun.un_tty); } - return chars; + /* + * Make sure that our cached values reflect the current reality. + */ + if (virt_carrier == 1) + ch->ch_flags |= CH_FCAR; + else + ch->ch_flags &= ~CH_FCAR; + + if (phys_carrier == 1) + ch->ch_flags |= CH_CD; + else + ch->ch_flags &= ~CH_CD; } -static int dgap_wait_for_drain(struct tty_struct *tty) +/*======================================================================= + * + * dgap_event - FEP to host event processing routine. + * + * bd - Board of current event. + * + *=======================================================================*/ +static int dgap_event(struct board_t *bd) { struct channel_t *ch; - struct un_t *un; + ulong lock_flags; + ulong lock_flags2; struct bs_t __iomem *bs; - int ret = 0; - uint count = 1; - ulong lock_flags = 0; + u8 __iomem *event; + u8 __iomem *vaddr; + struct ev_t __iomem *eaddr; + uint head; + uint tail; + int port; + int reason; + int modem; + int b1; - if (!tty || tty->magic != TTY_MAGIC) + if (!bd || bd->magic != DGAP_BOARD_MAGIC) return -EIO; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; + spin_lock_irqsave(&bd->bd_lock, lock_flags); - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; + vaddr = bd->re_map_membase; - bs = ch->ch_bs; - if (!bs) + if (!vaddr) { + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); return -EIO; + } - /* Loop until data is drained */ - while (count != 0) { - - count = dgap_tty_chars_in_buffer(tty); + eaddr = (struct ev_t __iomem *) (vaddr + EVBUF); - if (count == 0) - break; + /* Get our head and tail */ + head = readw(&(eaddr->ev_head)); + tail = readw(&(eaddr->ev_tail)); - /* Set flag waiting for drain */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + /* + * Forget it if pointers out of range. + */ - /* Go to sleep till we get woken up */ - ret = wait_event_interruptible(un->un_flags_wait, - ((un->un_flags & UN_EMPTY) == 0)); - /* If ret is non-zero, user ctrl-c'ed us */ - if (ret) - break; + if (head >= EVMAX - EVSTART || tail >= EVMAX - EVSTART || + (head | tail) & 03) { + /* Let go of board lock */ + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return -EIO; } - spin_lock_irqsave(&ch->ch_lock, lock_flags); - un->un_flags &= ~(UN_EMPTY); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return ret; -} - -/* - * dgap_maxcps_room - * - * Reduces bytes_available to the max number of characters - * that can be sent currently given the maxcps value, and - * returns the new bytes_available. This only affects printer - * output. - */ -static int dgap_maxcps_room(struct channel_t *ch, struct un_t *un, - int bytes_available) -{ /* - * If its not the Transparent print device, return - * the full data amount. + * Loop to process all the events in the buffer. */ - if (un->un_type != DGAP_PRINT) - return bytes_available; + while (tail != head) { - if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0) { - int cps_limit = 0; - unsigned long current_time = jiffies; - unsigned long buffer_time = current_time + - (HZ * ch->ch_digi.digi_bufsize) / - ch->ch_digi.digi_maxcps; + /* + * Get interrupt information. + */ - if (ch->ch_cpstime < current_time) { - /* buffer is empty */ - ch->ch_cpstime = current_time; /* reset ch_cpstime */ - cps_limit = ch->ch_digi.digi_bufsize; - } else if (ch->ch_cpstime < buffer_time) { - /* still room in the buffer */ - cps_limit = ((buffer_time - ch->ch_cpstime) * - ch->ch_digi.digi_maxcps) / HZ; - } else { - /* no room in the buffer */ - cps_limit = 0; - } + event = bd->re_map_membase + tail + EVSTART; - bytes_available = min(cps_limit, bytes_available); - } + port = ioread8(event); + reason = ioread8(event + 1); + modem = ioread8(event + 2); + b1 = ioread8(event + 3); - return bytes_available; -} + /* + * Make sure the interrupt is valid. + */ + if (port >= bd->nasync) + goto next; -static inline void dgap_set_firmware_event(struct un_t *un, unsigned int event) -{ - struct channel_t *ch; - struct bs_t __iomem *bs; + if (!(reason & (IFMODEM | IFBREAK | IFTLW | IFTEM | IFDATA))) + goto next; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - bs = ch->ch_bs; - if (!bs) - return; + ch = bd->channels[port]; - if ((event & UN_LOW) != 0) { - if ((un->un_flags & UN_LOW) == 0) { - un->un_flags |= UN_LOW; - writeb(1, &(bs->ilow)); - } - } - if ((event & UN_LOW) != 0) { - if ((un->un_flags & UN_EMPTY) == 0) { - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + goto next; + + /* + * If we have made it here, the event was valid. + * Lock down the channel. + */ + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + + bs = ch->ch_bs; + + if (!bs) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + goto next; } - } -} -/* - * dgap_tty_write_room() - * - * Return space available in Tx buffer - */ -static int dgap_tty_write_room(struct tty_struct *tty) -{ - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - u16 head, tail, tmask; - int ret; - ulong lock_flags = 0; + /* + * Process received data. + */ + if (reason & IFDATA) { - if (!tty) - return 0; + /* + * ALL LOCKS *MUST* BE DROPPED BEFORE CALLING INPUT! + * input could send some data to ld, which in turn + * could do a callback to one of our other functions. + */ + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; + dgap_input(ch); - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - bs = ch->ch_bs; - if (!bs) - return 0; + if (ch->ch_flags & CH_RACTIVE) + ch->ch_flags |= CH_RENABLE; + else + writeb(1, &(bs->idata)); - spin_lock_irqsave(&ch->ch_lock, lock_flags); + if (ch->ch_flags & CH_RWAIT) { + ch->ch_flags &= ~CH_RWAIT; - tmask = ch->ch_tsize - 1; - head = readw(&(bs->tx_head)) & tmask; - tail = readw(&(bs->tx_tail)) & tmask; + wake_up_interruptible + (&ch->ch_tun.un_flags_wait); + } + } - ret = tail - head - 1; - if (ret < 0) - ret += ch->ch_tsize; + /* + * Process Modem change signals. + */ + if (reason & IFMODEM) { + ch->ch_mistat = modem; + dgap_carrier(ch); + } + + /* + * Process break. + */ + if (reason & IFBREAK) { + + if (ch->ch_tun.un_tty) { + /* A break has been indicated */ + ch->ch_err_break++; + tty_buffer_request_room + (ch->ch_tun.un_tty->port, 1); + tty_insert_flip_char(ch->ch_tun.un_tty->port, + 0, TTY_BREAK); + tty_flip_buffer_push(ch->ch_tun.un_tty->port); + } + } - /* Limit printer to maxcps */ - ret = dgap_maxcps_room(ch, un, ret); + /* + * Process Transmit low. + */ + if (reason & IFTLW) { + dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_LOW, + &lock_flags, &lock_flags2); + dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_LOW, + &lock_flags, &lock_flags2); + if (ch->ch_flags & CH_WLOW) { + ch->ch_flags &= ~CH_WLOW; + wake_up_interruptible(&ch->ch_flags_wait); + } + } - /* - * If we are printer device, leave space for - * possibly both the on and off strings. - */ - if (un->un_type == DGAP_PRINT) { - if (!(ch->ch_flags & CH_PRON)) - ret -= ch->ch_digi.digi_onlen; - ret -= ch->ch_digi.digi_offlen; - } else { - if (ch->ch_flags & CH_PRON) - ret -= ch->ch_digi.digi_offlen; - } + /* + * Process Transmit empty. + */ + if (reason & IFTEM) { + dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_EMPTY, + &lock_flags, &lock_flags2); + dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_EMPTY, + &lock_flags, &lock_flags2); + if (ch->ch_flags & CH_WEMPTY) { + ch->ch_flags &= ~CH_WEMPTY; + wake_up_interruptible(&ch->ch_flags_wait); + } + } - if (ret < 0) - ret = 0; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - /* - * Schedule FEP to wake us up if needed. - * - * TODO: This might be overkill... - * Do we really need to schedule callbacks from the FEP - * in every case? Can we get smarter based on ret? - */ - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); +next: + tail = (tail + 4) & (EVMAX - EVSTART - 4); + } - return ret; -} + writew(tail, &(eaddr->ev_tail)); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -/* - * dgap_tty_put_char() - * - * Put a character into ch->ch_buf - * - * - used by the line discipline for OPOST processing - */ -static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c) -{ - /* - * Simply call tty_write. - */ - dgap_tty_write(tty, &c, 1); - return 1; + return 0; } /* - * dgap_tty_write() - * - * Take data from the user or kernel and send it out to the FEP. - * In here exists all the Transparent Print magic as well. + * Our board poller function. */ -static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, - int count) +static void dgap_poll_tasklet(unsigned long data) { - struct channel_t *ch; - struct un_t *un; - struct bs_t __iomem *bs; - char __iomem *vaddr; - u16 head, tail, tmask, remain; - int bufcount, n; + struct board_t *bd = (struct board_t *) data; ulong lock_flags; + char __iomem *vaddr; + u16 head, tail; - if (!tty) - return 0; + if (!bd || (bd->magic != DGAP_BOARD_MAGIC)) + return; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; + if (bd->inhibit_poller) + return; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; + spin_lock_irqsave(&bd->bd_lock, lock_flags); - bs = ch->ch_bs; - if (!bs) - return 0; + vaddr = bd->re_map_membase; - if (!count) - return 0; + /* + * If board is ready, parse deeper to see if there is anything to do. + */ + if (bd->state == BOARD_READY) { - spin_lock_irqsave(&ch->ch_lock, lock_flags); + struct ev_t __iomem *eaddr; - /* Get our space available for the channel from the board */ - tmask = ch->ch_tsize - 1; - head = readw(&(bs->tx_head)) & tmask; - tail = readw(&(bs->tx_tail)) & tmask; + if (!bd->re_map_membase) { + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return; + } + if (!bd->re_map_port) { + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return; + } - bufcount = tail - head - 1; - if (bufcount < 0) - bufcount += ch->ch_tsize; + if (!bd->nasync) + goto out; - /* - * Limit printer output to maxcps overall, with bursts allowed - * up to bufsize characters. - */ - bufcount = dgap_maxcps_room(ch, un, bufcount); + eaddr = (struct ev_t __iomem *) (vaddr + EVBUF); - /* - * Take minimum of what the user wants to send, and the - * space available in the FEP buffer. - */ - count = min(count, bufcount); + /* Get our head and tail */ + head = readw(&(eaddr->ev_head)); + tail = readw(&(eaddr->ev_tail)); - /* - * Bail if no space left. - */ - if (count <= 0) { - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - return 0; - } + /* + * If there is an event pending. Go service it. + */ + if (head != tail) { + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + dgap_event(bd); + spin_lock_irqsave(&bd->bd_lock, lock_flags); + } - /* - * Output the printer ON string, if we are in terminal mode, but - * need to be in printer mode. - */ - if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_onstr, - (int) ch->ch_digi.digi_onlen); - head = readw(&(bs->tx_head)) & tmask; - ch->ch_flags |= CH_PRON; - } +out: + /* + * If board is doing interrupts, ACK the interrupt. + */ + if (bd && bd->intr_running) + readb(bd->re_map_port + 2); - /* - * On the other hand, output the printer OFF string, if we are - * currently in printer mode, but need to output to the terminal. - */ - if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int) ch->ch_digi.digi_offlen); - head = readw(&(bs->tx_head)) & tmask; - ch->ch_flags &= ~CH_PRON; + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return; } - n = count; + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); +} - /* - * If the write wraps over the top of the circular buffer, - * move the portion up to the wrap point, and reset the - * pointers to the bottom. - */ - remain = ch->ch_tstart + ch->ch_tsize - head; +/* + * dgap_found_board() + * + * A board has been found, init it. + */ +static struct board_t *dgap_found_board(struct pci_dev *pdev, int id, + int boardnum) +{ + struct board_t *brd; + unsigned int pci_irq; + int i; + int ret; - if (n >= remain) { - n -= remain; - vaddr = ch->ch_taddr + head; + /* get the board structure and prep it */ + brd = kzalloc(sizeof(struct board_t), GFP_KERNEL); + if (!brd) + return ERR_PTR(-ENOMEM); - memcpy_toio(vaddr, (u8 *) buf, remain); + /* store the info for the board we've found */ + brd->magic = DGAP_BOARD_MAGIC; + brd->boardnum = boardnum; + brd->vendor = dgap_pci_tbl[id].vendor; + brd->device = dgap_pci_tbl[id].device; + brd->pdev = pdev; + brd->pci_bus = pdev->bus->number; + brd->pci_slot = PCI_SLOT(pdev->devfn); + brd->name = dgap_ids[id].name; + brd->maxports = dgap_ids[id].maxports; + brd->type = dgap_ids[id].config_type; + brd->dpatype = dgap_ids[id].dpatype; + brd->dpastatus = BD_NOFEP; + init_waitqueue_head(&brd->state_wait); - head = ch->ch_tstart; - buf += remain; - } + spin_lock_init(&brd->bd_lock); - if (n > 0) { + brd->inhibit_poller = FALSE; + brd->wait_for_bios = 0; + brd->wait_for_fep = 0; - /* - * Move rest of data. - */ - vaddr = ch->ch_taddr + head; - remain = n; + for (i = 0; i < MAXPORTS; i++) + brd->channels[i] = NULL; + + /* store which card & revision we have */ + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice); + pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev); + + pci_irq = pdev->irq; + brd->irq = pci_irq; - memcpy_toio(vaddr, (u8 *) buf, remain); - head += remain; + /* get the PCI Base Address Registers */ + /* Xr Jupiter and EPC use BAR 2 */ + if (brd->device == PCI_DEV_XRJ_DID || brd->device == PCI_DEV_EPCJ_DID) { + brd->membase = pci_resource_start(pdev, 2); + brd->membase_end = pci_resource_end(pdev, 2); + } + /* Everyone else uses BAR 0 */ + else { + brd->membase = pci_resource_start(pdev, 0); + brd->membase_end = pci_resource_end(pdev, 0); } - if (count) { - ch->ch_txcount += count; - head &= tmask; - writew(head, &(bs->tx_head)); + if (!brd->membase) { + ret = -ENODEV; + goto free_brd; } - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + if (brd->membase & 1) + brd->membase &= ~3; + else + brd->membase &= ~15; /* - * If this is the print device, and the - * printer is still on, we need to turn it - * off before going idle. If the buffer is - * non-empty, wait until it goes empty. - * Otherwise turn it off right now. + * On the PCI boards, there is no IO space allocated + * The I/O registers will be in the first 3 bytes of the + * upper 2MB of the 4MB memory space. The board memory + * will be mapped into the low 2MB of the 4MB memory space */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { - tail = readw(&(bs->tx_tail)) & tmask; - - if (tail != head) { - un->un_flags |= UN_EMPTY; - writeb(1, &(bs->iempty)); - } else { - dgap_wmove(ch, ch->ch_digi.digi_offstr, - (int) ch->ch_digi.digi_offlen); - head = readw(&(bs->tx_head)) & tmask; - ch->ch_flags &= ~CH_PRON; - } - } - - /* Update printer buffer empty time. */ - if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0) - && (ch->ch_digi.digi_bufsize > 0)) { - ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return count; -} + brd->port = brd->membase + PCI_IO_OFFSET; + brd->port_end = brd->port + PCI_IO_SIZE; -/* - * Return modem signals to ld. - */ -static int dgap_tty_tiocmget(struct tty_struct *tty) -{ - struct channel_t *ch; - struct un_t *un; - int result; - u8 mstat; - ulong lock_flags; + /* + * Special initialization for non-PLX boards + */ + if (brd->device != PCI_DEV_XRJ_DID && brd->device != PCI_DEV_EPCJ_DID) { + unsigned short cmd; - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; + pci_write_config_byte(pdev, 0x40, 0); + pci_write_config_byte(pdev, 0x46, 0); - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; + /* Limit burst length to 2 doubleword transactions */ + pci_write_config_byte(pdev, 0x42, 1); - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; + /* + * Enable IO and mem if not already done. + * This was needed for support on Itanium. + */ + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } - spin_lock_irqsave(&ch->ch_lock, lock_flags); + /* init our poll helper tasklet */ + tasklet_init(&brd->helper_tasklet, dgap_poll_tasklet, + (unsigned long) brd); - mstat = readb(&(ch->ch_bs->m_stat)); - /* Append any outbound signals that might be pending... */ - mstat |= ch->ch_mostat; + ret = dgap_remap(brd); + if (ret) + goto free_brd; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + pr_info("dgap: board %d: %s (rev %d), irq %ld\n", + boardnum, brd->name, brd->rev, brd->irq); - result = 0; + return brd; - if (mstat & D_DTR(ch)) - result |= TIOCM_DTR; - if (mstat & D_RTS(ch)) - result |= TIOCM_RTS; - if (mstat & D_CTS(ch)) - result |= TIOCM_CTS; - if (mstat & D_DSR(ch)) - result |= TIOCM_DSR; - if (mstat & D_RI(ch)) - result |= TIOCM_RI; - if (mstat & D_CD(ch)) - result |= TIOCM_CD; +free_brd: + kfree(brd); - return result; + return ERR_PTR(ret); } /* - * dgap_tty_tiocmset() + * dgap_intr() * - * Set modem signals, called by ld. + * Driver interrupt handler. */ -static int dgap_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) +static irqreturn_t dgap_intr(int irq, void *voidbrd) { - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; + struct board_t *brd = voidbrd; - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; + if (!brd) + return IRQ_NONE; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; + /* + * Check to make sure its for us. + */ + if (brd->magic != DGAP_BOARD_MAGIC) + return IRQ_NONE; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; + brd->intr_count++; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -EIO; + /* + * Schedule tasklet to run at a better time. + */ + tasklet_schedule(&brd->helper_tasklet); + return IRQ_HANDLED; +} - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); +/***************************************************************************** +* +* Function: +* +* dgap_poll_handler +* +* Author: +* +* Scott H Kilau +* +* Parameters: +* +* dummy -- ignored +* +* Return Values: +* +* none +* +* Description: +* +* As each timer expires, it determines (a) whether the "transmit" +* waiter needs to be woken up, and (b) whether the poller needs to +* be rescheduled. +* +******************************************************************************/ - if (set & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } +static void dgap_poll_handler(ulong dummy) +{ + unsigned int i; + struct board_t *brd; + unsigned long lock_flags; + ulong new_time; - if (set & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval |= D_DTR(ch); - } + dgap_poll_counter++; - if (clear & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval &= ~(D_RTS(ch)); - } + /* + * Do not start the board state machine until + * driver tells us its up and running, and has + * everything it needs. + */ + if (dgap_driver_state != DRIVER_READY) + goto schedule_poller; - if (clear & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval &= ~(D_DTR(ch)); - } + /* + * If we have just 1 board, or the system is not SMP, + * then use the typical old style poller. + * Otherwise, use our new tasklet based poller, which should + * speed things up for multiple boards. + */ + if ((dgap_numboards == 1) || (num_online_cpus() <= 1)) { + for (i = 0; i < dgap_numboards; i++) { - dgap_param(ch, bd, un->un_type); + brd = dgap_board[i]; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (brd->state == BOARD_FAILED) + continue; + if (!brd->intr_running) + /* Call the real board poller directly */ + dgap_poll_tasklet((unsigned long) brd); + } + } else { + /* + * Go thru each board, kicking off a + * tasklet for each if needed + */ + for (i = 0; i < dgap_numboards; i++) { + brd = dgap_board[i]; + + /* + * Attempt to grab the board lock. + * + * If we can't get it, no big deal, the next poll + * will get it. Basically, I just really don't want + * to spin in here, because I want to kick off my + * tasklets as fast as I can, and then get out the + * poller. + */ + if (!spin_trylock(&brd->bd_lock)) + continue; - return 0; -} + /* + * If board is in a failed state, don't bother + * scheduling a tasklet + */ + if (brd->state == BOARD_FAILED) { + spin_unlock(&brd->bd_lock); + continue; + } -/* - * dgap_tty_send_break() - * - * Send a Break, called by ld. - */ -static int dgap_tty_send_break(struct tty_struct *tty, int msec) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; + /* Schedule a poll helper task */ + if (!brd->intr_running) + tasklet_schedule(&brd->helper_tasklet); - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; + /* + * Can't do DGAP_UNLOCK here, as we don't have + * lock_flags because we did a trylock above. + */ + spin_unlock(&brd->bd_lock); + } + } - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; +schedule_poller: - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; + /* + * Schedule ourself back at the nominal wakeup interval. + */ + spin_lock_irqsave(&dgap_poll_lock, lock_flags); + dgap_poll_time += dgap_jiffies_from_ms(dgap_poll_tick); - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -EIO; + new_time = dgap_poll_time - jiffies; - switch (msec) { - case -1: - msec = 0xFFFF; - break; - case 0: - msec = 1; - break; - default: - msec /= 10; - break; + if ((ulong) new_time >= 2 * dgap_poll_tick) { + dgap_poll_time = + jiffies + dgap_jiffies_from_ms(dgap_poll_tick); } - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); -#if 0 - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); -#endif - dgap_cmdw(ch, SBREAK, (u16) msec, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + dgap_poll_timer.function = dgap_poll_handler; + dgap_poll_timer.data = 0; + dgap_poll_timer.expires = dgap_poll_time; + spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - return 0; + if (!dgap_poll_stop) + add_timer(&dgap_poll_timer); } -/* - * dgap_tty_wait_until_sent() +/*======================================================================= * - * wait until data has been transmitted, called by ld. - */ -static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - dgap_wait_for_drain(tty); -} - -/* - * dgap_send_xchar() + * dgap_cmdb - Sends a 2 byte command to the FEP. * - * send a high priority character, called by ld. - */ -static void dgap_tty_send_xchar(struct tty_struct *tty, char c) + * ch - Pointer to channel structure. + * cmd - Command to be sent. + * byte1 - Integer containing first byte to be sent. + * byte2 - Integer containing second byte to be sent. + * ncmds - Wait until ncmds or fewer cmds are left + * in the cmd buffer before returning. + * + *=======================================================================*/ +static void dgap_cmdb(struct channel_t *ch, u8 cmd, u8 byte1, + u8 byte2, uint ncmds) { - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; + char __iomem *vaddr; + struct __iomem cm_t *cm_addr; + uint count; + uint n; + u16 head; + u16 tail; - if (!tty || tty->magic != TTY_MAGIC) + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) return; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) + /* + * Check if board is still alive. + */ + if (ch->ch_bd->state == BOARD_FAILED) return; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + /* + * Make sure the pointers are in range before + * writing to the FEP memory. + */ + vaddr = ch->ch_bd->re_map_membase; + + if (!vaddr) return; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) + cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); + + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + ch->ch_bd->state = BOARD_FAILED; return; + } - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + /* + * Put the data in the circular command buffer. + */ + writeb(cmd, (vaddr + head + CMDSTART + 0)); + writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1)); + writeb(byte1, (vaddr + head + CMDSTART + 2)); + writeb(byte2, (vaddr + head + CMDSTART + 3)); + + head = (head + 4) & (CMDMAX - CMDSTART - 4); + + writew(head, &(cm_addr->cm_head)); /* - * This is technically what we should do. - * However, the NIST tests specifically want - * to see each XON or XOFF character that it - * sends, so lets just send each character - * by hand... + * Wait if necessary before updating the head + * pointer to limit the number of outstanding + * commands to the FEP. If the time spent waiting + * is outlandish, declare the FEP dead. */ -#if 0 - if (c == STOP_CHAR(tty)) - dgap_cmdw(ch, RPAUSE, 0, 0); - else if (c == START_CHAR(tty)) - dgap_cmdw(ch, RRESUME, 0, 0); - else - dgap_wmove(ch, &c, 1); -#else - dgap_wmove(ch, &c, 1); -#endif + for (count = dgap_count ;;) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} + head = readw(&(cm_addr->cm_head)); + tail = readw(&(cm_addr->cm_tail)); -/* - * Return modem signals to ld. - */ -static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value) -{ - int result; - u8 mstat; - ulong lock_flags; - int rc; + n = (head - tail) & (CMDMAX - CMDSTART - 4); - spin_lock_irqsave(&ch->ch_lock, lock_flags); + if (n <= ncmds * sizeof(struct cm_t)) + break; - mstat = readb(&(ch->ch_bs->m_stat)); - /* Append any outbound signals that might be pending... */ - mstat |= ch->ch_mostat; + if (--count == 0) { + ch->ch_bd->state = BOARD_FAILED; + return; + } + udelay(10); + } +} - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); +/*======================================================================= + * + * dgap_cmdw - Sends a 1 word command to the FEP. + * + * ch - Pointer to channel structure. + * cmd - Command to be sent. + * word - Integer containing word to be sent. + * ncmds - Wait until ncmds or fewer cmds are left + * in the cmd buffer before returning. + * + *=======================================================================*/ +static void dgap_cmdw(struct channel_t *ch, u8 cmd, u16 word, uint ncmds) +{ + char __iomem *vaddr; + struct __iomem cm_t *cm_addr; + uint count; + uint n; + u16 head; + u16 tail; - result = 0; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - if (mstat & D_DTR(ch)) - result |= TIOCM_DTR; - if (mstat & D_RTS(ch)) - result |= TIOCM_RTS; - if (mstat & D_CTS(ch)) - result |= TIOCM_CTS; - if (mstat & D_DSR(ch)) - result |= TIOCM_DSR; - if (mstat & D_RI(ch)) - result |= TIOCM_RI; - if (mstat & D_CD(ch)) - result |= TIOCM_CD; + /* + * Check if board is still alive. + */ + if (ch->ch_bd->state == BOARD_FAILED) + return; + + /* + * Make sure the pointers are in range before + * writing to the FEP memory. + */ + vaddr = ch->ch_bd->re_map_membase; + if (!vaddr) + return; - rc = put_user(result, value); + cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); - return rc; -} + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + ch->ch_bd->state = BOARD_FAILED; + return; + } -/* - * dgap_set_modem_info() - * - * Set modem signals, called by ld. - */ -static int dgap_set_modem_info(struct channel_t *ch, struct board_t *bd, - struct un_t *un, unsigned int command, - unsigned int __user *value) -{ - int ret; - unsigned int arg; - ulong lock_flags; - ulong lock_flags2; + /* + * Put the data in the circular command buffer. + */ + writeb(cmd, (vaddr + head + CMDSTART + 0)); + writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1)); + writew((u16) word, (vaddr + head + CMDSTART + 2)); - ret = get_user(arg, value); - if (ret) - return ret; + head = (head + 4) & (CMDMAX - CMDSTART - 4); - switch (command) { - case TIOCMBIS: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } + writew(head, &(cm_addr->cm_head)); - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval |= D_DTR(ch); - } + /* + * Wait if necessary before updating the head + * pointer to limit the number of outstanding + * commands to the FEP. If the time spent waiting + * is outlandish, declare the FEP dead. + */ + for (count = dgap_count ;;) { - break; + head = readw(&(cm_addr->cm_head)); + tail = readw(&(cm_addr->cm_tail)); - case TIOCMBIC: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval &= ~(D_RTS(ch)); - } + n = (head - tail) & (CMDMAX - CMDSTART - 4); - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval &= ~(D_DTR(ch)); + if (n <= ncmds * sizeof(struct cm_t)) + break; + + if (--count == 0) { + ch->ch_bd->state = BOARD_FAILED; + return; } + udelay(10); + } +} - break; +/*======================================================================= + * + * dgap_cmdw_ext - Sends a extended word command to the FEP. + * + * ch - Pointer to channel structure. + * cmd - Command to be sent. + * word - Integer containing word to be sent. + * ncmds - Wait until ncmds or fewer cmds are left + * in the cmd buffer before returning. + * + *=======================================================================*/ +static void dgap_cmdw_ext(struct channel_t *ch, u16 cmd, u16 word, uint ncmds) +{ + char __iomem *vaddr; + struct __iomem cm_t *cm_addr; + uint count; + uint n; + u16 head; + u16 tail; - case TIOCMSET: - ch->ch_mforce = D_DTR(ch)|D_RTS(ch); + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - if (arg & TIOCM_RTS) - ch->ch_mval |= D_RTS(ch); - else - ch->ch_mval &= ~(D_RTS(ch)); + /* + * Check if board is still alive. + */ + if (ch->ch_bd->state == BOARD_FAILED) + return; - if (arg & TIOCM_DTR) - ch->ch_mval |= (D_DTR(ch)); - else - ch->ch_mval &= ~(D_DTR(ch)); + /* + * Make sure the pointers are in range before + * writing to the FEP memory. + */ + vaddr = ch->ch_bd->re_map_membase; + if (!vaddr) + return; - break; + cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); - default: - return -EINVAL; + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + ch->ch_bd->state = BOARD_FAILED; + return; } - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + /* + * Put the data in the circular command buffer. + */ - dgap_param(ch, bd, un->un_type); + /* Write an FF to tell the FEP that we want an extended command */ + writeb((u8) 0xff, (vaddr + head + CMDSTART + 0)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1)); + writew((u16) cmd, (vaddr + head + CMDSTART + 2)); - return 0; -} + /* + * If the second part of the command won't fit, + * put it at the beginning of the circular buffer. + */ + if (((head + 4) >= ((CMDMAX - CMDSTART)) || (head & 03))) + writew((u16) word, (vaddr + CMDSTART)); + else + writew((u16) word, (vaddr + head + CMDSTART + 4)); -/* - * dgap_tty_digigeta() - * - * Ioctl to get the information for ditty. - * - * - * - */ -static int dgap_tty_digigeta(struct channel_t *ch, - struct digi_t __user *retinfo) -{ - struct digi_t tmp; - ulong lock_flags; + head = (head + 8) & (CMDMAX - CMDSTART - 4); - if (!retinfo) - return -EFAULT; + writew(head, &(cm_addr->cm_head)); - memset(&tmp, 0, sizeof(tmp)); + /* + * Wait if necessary before updating the head + * pointer to limit the number of outstanding + * commands to the FEP. If the time spent waiting + * is outlandish, declare the FEP dead. + */ + for (count = dgap_count ;;) { - spin_lock_irqsave(&ch->ch_lock, lock_flags); - memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + head = readw(&(cm_addr->cm_head)); + tail = readw(&(cm_addr->cm_tail)); - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; + n = (head - tail) & (CMDMAX - CMDSTART - 4); - return 0; + if (n <= ncmds * sizeof(struct cm_t)) + break; + + if (--count == 0) { + ch->ch_bd->state = BOARD_FAILED; + return; + } + udelay(10); + } } -/* - * dgap_tty_digiseta() - * - * Ioctl to set the information for ditty. +/*======================================================================= * + * dgap_wmove - Write data to FEP buffer. * + * ch - Pointer to channel structure. + * buf - Poiter to characters to be moved. + * cnt - Number of characters to move. * - */ -static int dgap_tty_digiseta(struct channel_t *ch, struct board_t *bd, - struct un_t *un, struct digi_t __user *new_info) + *=======================================================================*/ +static void dgap_wmove(struct channel_t *ch, char *buf, uint cnt) { - struct digi_t new_digi; - ulong lock_flags = 0; - unsigned long lock_flags2; - - if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) - return -EFAULT; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - memcpy(&ch->ch_digi, &new_digi, sizeof(struct digi_t)); - - if (ch->ch_digi.digi_maxcps < 1) - ch->ch_digi.digi_maxcps = 1; - - if (ch->ch_digi.digi_maxcps > 10000) - ch->ch_digi.digi_maxcps = 10000; - - if (ch->ch_digi.digi_bufsize < 10) - ch->ch_digi.digi_bufsize = 10; + int n; + char __iomem *taddr; + struct bs_t __iomem *bs; + u16 head; - if (ch->ch_digi.digi_maxchar < 1) - ch->ch_digi.digi_maxchar = 1; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) - ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; + /* + * Check parameters. + */ + bs = ch->ch_bs; + head = readw(&(bs->tx_head)); - if (ch->ch_digi.digi_onlen > DIGI_PLEN) - ch->ch_digi.digi_onlen = DIGI_PLEN; + /* + * If pointers are out of range, just return. + */ + if ((cnt > ch->ch_tsize) || + (unsigned)(head - ch->ch_tstart) >= ch->ch_tsize) + return; - if (ch->ch_digi.digi_offlen > DIGI_PLEN) - ch->ch_digi.digi_offlen = DIGI_PLEN; + /* + * If the write wraps over the top of the circular buffer, + * move the portion up to the wrap point, and reset the + * pointers to the bottom. + */ + n = ch->ch_tstart + ch->ch_tsize - head; - dgap_param(ch, bd, un->un_type); + if (cnt >= n) { + cnt -= n; + taddr = ch->ch_taddr + head; + memcpy_toio(taddr, buf, n); + head = ch->ch_tstart; + buf += n; + } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + /* + * Move rest of data. + */ + taddr = ch->ch_taddr + head; + n = cnt; + memcpy_toio(taddr, buf, n); + head += cnt; - return 0; + writew(head, &(bs->tx_head)); } /* - * dgap_tty_digigetedelay() - * - * Ioctl to get the current edelay setting. - * - * - * + * Calls the firmware to reset this channel. */ -static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo) +static void dgap_firmware_reset_port(struct channel_t *ch) { - struct channel_t *ch; - struct un_t *un; - int tmp; - ulong lock_flags; - - if (!retinfo) - return -EFAULT; - - if (!tty || tty->magic != TTY_MAGIC) - return -EFAULT; + dgap_cmdb(ch, CHRESET, 0, 0, 0); - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EFAULT; + /* + * Now that the channel is reset, we need to make sure + * all the current settings get reapplied to the port + * in the firmware. + * + * So we will set the driver's cache of firmware + * settings all to 0, and then call param. + */ + ch->ch_fepiflag = 0; + ch->ch_fepcflag = 0; + ch->ch_fepoflag = 0; + ch->ch_fepstartc = 0; + ch->ch_fepstopc = 0; + ch->ch_fepastartc = 0; + ch->ch_fepastopc = 0; + ch->ch_mostat = 0; + ch->ch_hflow = 0; +} - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EFAULT; +/*======================================================================= + * + * dgap_param - Set Digi parameters. + * + * struct tty_struct * - TTY for port. + * + *=======================================================================*/ +static int dgap_param(struct channel_t *ch, struct board_t *bd, u32 un_type) +{ + u16 head; + u16 cflag; + u16 iflag; + u8 mval; + u8 hflow; - memset(&tmp, 0, sizeof(tmp)); + /* + * If baud rate is zero, flush queues, and set mval to drop DTR. + */ + if ((ch->ch_c_cflag & (CBAUD)) == 0) { - spin_lock_irqsave(&ch->ch_lock, lock_flags); - tmp = readw(&(ch->ch_bs->edelay)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + /* flush rx */ + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; + /* flush tx */ + head = readw(&(ch->ch_bs->tx_head)); + writew(head, &(ch->ch_bs->tx_tail)); - return 0; -} + ch->ch_flags |= (CH_BAUD0); -/* - * dgap_tty_digisetedelay() - * - * Ioctl to set the EDELAY setting - * - */ -static int dgap_tty_digisetedelay(struct channel_t *ch, struct board_t *bd, - struct un_t *un, int __user *new_info) -{ - int new_digi; - ulong lock_flags; - ulong lock_flags2; + /* Drop RTS and DTR */ + ch->ch_mval &= ~(D_RTS(ch)|D_DTR(ch)); + mval = D_DTR(ch) | D_RTS(ch); + ch->ch_baud_info = 0; - if (copy_from_user(&new_digi, new_info, sizeof(int))) - return -EFAULT; + } else if (ch->ch_custom_speed && (bd->bd_flags & BD_FEP5PLUS)) { + /* + * Tell the fep to do the command + */ - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + dgap_cmdw_ext(ch, 0xff01, ch->ch_custom_speed, 0); - writew((u16) new_digi, &(ch->ch_bs->edelay)); + /* + * Now go get from fep mem, what the fep + * believes the custom baud rate is. + */ + ch->ch_custom_speed = dgap_get_custom_baud(ch); + ch->ch_baud_info = ch->ch_custom_speed; - dgap_param(ch, bd, un->un_type); + /* Handle transition from B0 */ + if (ch->ch_flags & CH_BAUD0) { + ch->ch_flags &= ~(CH_BAUD0); + ch->ch_mval |= (D_RTS(ch)|D_DTR(ch)); + } + mval = D_DTR(ch) | D_RTS(ch); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + } else { + /* + * Set baud rate, character size, and parity. + */ - return 0; -} -/* - * dgap_tty_digigetcustombaud() - * - * Ioctl to get the current custom baud rate setting. - */ -static int dgap_tty_digigetcustombaud(struct channel_t *ch, struct un_t *un, - int __user *retinfo) -{ - int tmp; - ulong lock_flags; + int iindex = 0; + int jindex = 0; + int baud = 0; - if (!retinfo) - return -EFAULT; + ulong bauds[4][16] = { + { /* slowbaud */ + 0, 50, 75, 110, + 134, 150, 200, 300, + 600, 1200, 1800, 2400, + 4800, 9600, 19200, 38400 }, + { /* slowbaud & CBAUDEX */ + 0, 57600, 115200, 230400, + 460800, 150, 200, 921600, + 600, 1200, 1800, 2400, + 4800, 9600, 19200, 38400 }, + { /* fastbaud */ + 0, 57600, 76800, 115200, + 14400, 57600, 230400, 76800, + 115200, 230400, 28800, 460800, + 921600, 9600, 19200, 38400 }, + { /* fastbaud & CBAUDEX */ + 0, 57600, 115200, 230400, + 460800, 150, 200, 921600, + 600, 1200, 1800, 2400, + 4800, 9600, 19200, 38400 } + }; - memset(&tmp, 0, sizeof(tmp)); + /* + * Only use the TXPrint baud rate if the + * terminal unit is NOT open + */ + if (!(ch->ch_tun.un_flags & UN_ISOPEN) && + un_type == DGAP_PRINT) + baud = C_BAUD(ch->ch_pun.un_tty) & 0xff; + else + baud = C_BAUD(ch->ch_tun.un_tty) & 0xff; - spin_lock_irqsave(&ch->ch_lock, lock_flags); - tmp = dgap_get_custom_baud(ch); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + if (ch->ch_c_cflag & CBAUDEX) + iindex = 1; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; + if (ch->ch_digi.digi_flags & DIGI_FAST) + iindex += 2; - return 0; -} + jindex = baud; -/* - * dgap_tty_digisetcustombaud() - * - * Ioctl to set the custom baud rate setting - */ -static int dgap_tty_digisetcustombaud(struct channel_t *ch, struct board_t *bd, - struct un_t *un, int __user *new_info) -{ - uint new_rate; - ulong lock_flags; - ulong lock_flags2; + if ((iindex >= 0) && (iindex < 4) && + (jindex >= 0) && (jindex < 16)) + baud = bauds[iindex][jindex]; + else + baud = 0; - if (copy_from_user(&new_rate, new_info, sizeof(unsigned int))) - return -EFAULT; + if (baud == 0) + baud = 9600; - if (bd->bd_flags & BD_FEP5PLUS) { + ch->ch_baud_info = baud; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + /* + * CBAUD has bit position 0x1000 set these days to + * indicate Linux baud rate remap. + * We use a different bit assignment for high speed. + * Clear this bit out while grabbing the parts of + * "cflag" we want. + */ + cflag = ch->ch_c_cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | + CSTOPB | CSIZE); - ch->ch_custom_speed = new_rate; + /* + * HUPCL bit is used by FEP to indicate fast baud + * table is to be used. + */ + if ((ch->ch_digi.digi_flags & DIGI_FAST) || + (ch->ch_c_cflag & CBAUDEX)) + cflag |= HUPCL; - dgap_param(ch, bd, un->un_type); + if ((ch->ch_c_cflag & CBAUDEX) && + !(ch->ch_digi.digi_flags & DIGI_FAST)) { + /* + * The below code is trying to guarantee that only + * baud rates 115200, 230400, 460800, 921600 are + * remapped. We use exclusive or because the various + * baud rates share common bit positions and therefore + * can't be tested for easily. + */ + tcflag_t tcflag = (ch->ch_c_cflag & CBAUD) | CBAUDEX; + int baudpart = 0; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - } + /* + * Map high speed requests to index + * into FEP's baud table + */ + switch (tcflag) { + case B57600: + baudpart = 1; + break; +#ifdef B76800 + case B76800: + baudpart = 2; + break; +#endif + case B115200: + baudpart = 3; + break; + case B230400: + baudpart = 9; + break; + case B460800: + baudpart = 11; + break; +#ifdef B921600 + case B921600: + baudpart = 12; + break; +#endif + default: + baudpart = 0; + } - return 0; -} + if (baudpart) + cflag = (cflag & ~(CBAUD | CBAUDEX)) | baudpart; + } -/* - * dgap_set_termios() - */ -static void dgap_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - unsigned long lock_flags; - unsigned long lock_flags2; + cflag &= 0xffff; - if (!tty || tty->magic != TTY_MAGIC) - return; + if (cflag != ch->ch_fepcflag) { + ch->ch_fepcflag = (u16) (cflag & 0xffff); - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; + /* + * Okay to have channel and board + * locks held calling this + */ + dgap_cmdw(ch, SCFLAG, (u16) cflag, 0); + } - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + /* Handle transition from B0 */ + if (ch->ch_flags & CH_BAUD0) { + ch->ch_flags &= ~(CH_BAUD0); + ch->ch_mval |= (D_RTS(ch)|D_DTR(ch)); + } + mval = D_DTR(ch) | D_RTS(ch); + } - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; + /* + * Get input flags. + */ + iflag = ch->ch_c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | + INPCK | ISTRIP | IXON | IXANY | IXOFF); - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + if ((ch->ch_startc == _POSIX_VDISABLE) || + (ch->ch_stopc == _POSIX_VDISABLE)) { + iflag &= ~(IXON | IXOFF); + ch->ch_c_iflag &= ~(IXON | IXOFF); + } - ch->ch_c_cflag = tty->termios.c_cflag; - ch->ch_c_iflag = tty->termios.c_iflag; - ch->ch_c_oflag = tty->termios.c_oflag; - ch->ch_c_lflag = tty->termios.c_lflag; - ch->ch_startc = tty->termios.c_cc[VSTART]; - ch->ch_stopc = tty->termios.c_cc[VSTOP]; + /* + * Only the IBM Xr card can switch between + * 232 and 422 modes on the fly + */ + if (bd->device == PCI_DEV_XR_IBM_DID) { + if (ch->ch_digi.digi_flags & DIGI_422) + dgap_cmdb(ch, SCOMMODE, MODE_422, 0, 0); + else + dgap_cmdb(ch, SCOMMODE, MODE_232, 0, 0); + } - dgap_carrier(ch); - dgap_param(ch, bd, un->un_type); + if (ch->ch_digi.digi_flags & DIGI_ALTPIN) + iflag |= IALTPIN; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} + if (iflag != ch->ch_fepiflag) { + ch->ch_fepiflag = iflag; -static void dgap_tty_throttle(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; + /* Okay to have channel and board locks held calling this */ + dgap_cmdw(ch, SIFLAG, (u16) ch->ch_fepiflag, 0); + } - if (!tty || tty->magic != TTY_MAGIC) - return; + /* + * Select hardware handshaking. + */ + hflow = 0; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; + if (ch->ch_c_cflag & CRTSCTS) + hflow |= (D_RTS(ch) | D_CTS(ch)); + if (ch->ch_digi.digi_flags & RTSPACE) + hflow |= D_RTS(ch); + if (ch->ch_digi.digi_flags & DTRPACE) + hflow |= D_DTR(ch); + if (ch->ch_digi.digi_flags & CTSPACE) + hflow |= D_CTS(ch); + if (ch->ch_digi.digi_flags & DSRPACE) + hflow |= D_DSR(ch); + if (ch->ch_digi.digi_flags & DCDPACE) + hflow |= D_CD(ch); - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + if (hflow != ch->ch_hflow) { + ch->ch_hflow = hflow; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SHFLOW, (u8) hflow, 0xff, 0); + } - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + /* + * Set RTS and/or DTR Toggle if needed, + * but only if product is FEP5+ based. + */ + if (bd->bd_flags & BD_FEP5PLUS) { + u16 hflow2 = 0; - ch->ch_flags |= (CH_RXBLOCK); -#if 1 - dgap_cmdw(ch, RPAUSE, 0, 0); -#endif + if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) + hflow2 |= (D_RTS(ch)); + if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) + hflow2 |= (D_DTR(ch)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + dgap_cmdw_ext(ch, 0xff03, hflow2, 0); + } -} + /* + * Set modem control lines. + */ -static void dgap_tty_unthrottle(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; + mval ^= ch->ch_mforce & (mval ^ ch->ch_mval); - if (!tty || tty->magic != TTY_MAGIC) - return; + if (ch->ch_mostat ^ mval) { + ch->ch_mostat = mval; - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SMODEM, (u8) mval, D_RTS(ch)|D_DTR(ch), 0); + } - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + /* + * Read modem signals, and then call carrier function. + */ + ch->ch_mistat = readb(&(ch->ch_bs->m_stat)); + dgap_carrier(ch); - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; + /* + * Set the start and stop characters. + */ + if (ch->ch_startc != ch->ch_fepstartc || + ch->ch_stopc != ch->ch_fepstopc) { + ch->ch_fepstartc = ch->ch_startc; + ch->ch_fepstopc = ch->ch_stopc; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SFLOWC, ch->ch_fepstartc, ch->ch_fepstopc, 0); + } - ch->ch_flags &= ~(CH_RXBLOCK); + /* + * Set the Auxiliary start and stop characters. + */ + if (ch->ch_astartc != ch->ch_fepastartc || + ch->ch_astopc != ch->ch_fepastopc) { + ch->ch_fepastartc = ch->ch_astartc; + ch->ch_fepastopc = ch->ch_astopc; -#if 1 - dgap_cmdw(ch, RRESUME, 0, 0); -#endif + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SAFLOWC, ch->ch_fepastartc, ch->ch_fepastopc, 0); + } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return 0; } -static void dgap_tty_start(struct tty_struct *tty) +/* + * dgap_block_til_ready() + * + * Wait for DCD, if needed. + */ +static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, + struct channel_t *ch) { - struct board_t *bd; - struct channel_t *ch; + int retval = 0; struct un_t *un; ulong lock_flags; - ulong lock_flags2; + uint old_flags; + int sleep_on_un_flags; - if (!tty || tty->magic != TTY_MAGIC) - return; + if (!tty || tty->magic != TTY_MAGIC || !file || !ch || + ch->magic != DGAP_CHANNEL_MAGIC) + return -EIO; un = tty->driver_data; if (!un || un->magic != DGAP_UNIT_MAGIC) - return; + return -EIO; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + spin_lock_irqsave(&ch->ch_lock, lock_flags); - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; + ch->ch_wopen++; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + /* Loop forever */ + while (1) { - dgap_cmdw(ch, RESUMETX, 0, 0); + sleep_on_un_flags = 0; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} + /* + * If board has failed somehow during our sleep, + * bail with error. + */ + if (ch->ch_bd->state == BOARD_FAILED) { + retval = -EIO; + break; + } -static void dgap_tty_stop(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; + /* If tty was hung up, break out of loop and set error. */ + if (tty_hung_up_p(file)) { + retval = -EAGAIN; + break; + } - if (!tty || tty->magic != TTY_MAGIC) - return; + /* + * If either unit is in the middle of the fragile part of close, + * we just cannot touch the channel safely. + * Go back to sleep, knowing that when the channel can be + * touched safely, the close routine will signal the + * ch_wait_flags to wake us back up. + */ + if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & + UN_CLOSING)) { - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; + /* + * Our conditions to leave cleanly and happily: + * 1) NONBLOCKING on the tty is set. + * 2) CLOCAL is set. + * 3) DCD (fake or real) is active. + */ - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + if (file->f_flags & O_NONBLOCK) + break; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; + if (tty->flags & (1 << TTY_IO_ERROR)) + break; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + if (ch->ch_flags & CH_CD) + break; - dgap_cmdw(ch, PAUSETX, 0, 0); + if (ch->ch_flags & CH_FCAR) + break; + } else { + sleep_on_un_flags = 1; + } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} + /* + * If there is a signal pending, the user probably + * interrupted (ctrl-c) us. + * Leave loop with error set. + */ + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } -/* - * dgap_tty_flush_chars() - * - * Flush the cook buffer - * - * Note to self, and any other poor souls who venture here: - * - * flush in this case DOES NOT mean dispose of the data. - * instead, it means "stop buffering and send it if you - * haven't already." Just guess how I figured that out... SRW 2-Jun-98 - * - * It is also always called in interrupt context - JAR 8-Sept-99 - */ -static void dgap_tty_flush_chars(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; + /* + * Store the flags before we let go of channel lock + */ + if (sleep_on_un_flags) + old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags; + else + old_flags = ch->ch_flags; - if (!tty || tty->magic != TTY_MAGIC) - return; + /* + * Let go of channel lock before calling schedule. + * Our poller will get any FEP events and wake us up when DCD + * eventually goes active. + */ - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + /* + * Wait for something in the flags to change + * from the current value. + */ + if (sleep_on_un_flags) { + retval = wait_event_interruptible(un->un_flags_wait, + (old_flags != (ch->ch_tun.un_flags | + ch->ch_pun.un_flags))); + } else { + retval = wait_event_interruptible(ch->ch_flags_wait, + (old_flags != ch->ch_flags)); + } - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; + /* + * We got woken up for some reason. + * Before looping around, grab our channel lock. + */ + spin_lock_irqsave(&ch->ch_lock, lock_flags); + } - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + ch->ch_wopen--; - /* TODO: Do something here */ + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return retval; } /* @@ -3609,2005 +3245,2126 @@ static void dgap_tty_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -/***************************************************************************** - * - * The IOCTL function and all of its helpers - * - *****************************************************************************/ - /* - * dgap_tty_ioctl() + * dgap_tty_hangup() * - * The usual assortment of ioctl's + * Hangup the port. Like a close, but don't wait for output to drain. */ -static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) +static void dgap_tty_hangup(struct tty_struct *tty) { struct board_t *bd; struct channel_t *ch; struct un_t *un; - int rc; - u16 head; - ulong lock_flags = 0; - ulong lock_flags2 = 0; - void __user *uarg = (void __user *) arg; if (!tty || tty->magic != TTY_MAGIC) - return -ENODEV; + return; un = tty->driver_data; if (!un || un->magic != DGAP_UNIT_MAGIC) - return -ENODEV; + return; ch = un->un_ch; if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -ENODEV; + return; bd = ch->ch_bd; if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -ENODEV; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - if (un->un_open_count <= 0) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EIO; - } - - switch (cmd) { - - /* Here are all the standard ioctl's that we MUST implement */ + return; - case TCSBRK: - /* - * TCSBRK is SVID version: non-zero arg --> no break - * this behaviour is exploited by tcdrain(). - * - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds so we'll ask for something - * in the middle: 0.375 seconds. - */ - rc = tty_check_change(tty); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (rc) - return rc; + /* flush the transmit queues */ + dgap_tty_flush_buffer(tty); +} - rc = dgap_wait_for_drain(tty); +/* + * dgap_tty_chars_in_buffer() + * + * Return number of characters that have not been transmitted yet. + * + * This routine is used by the line discipline to determine if there + * is data waiting to be transmitted/drained/flushed or not. + */ +static int dgap_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + struct bs_t __iomem *bs; + u8 tbusy; + uint chars; + u16 thead, ttail, tmask, chead, ctail; + ulong lock_flags = 0; + ulong lock_flags2 = 0; - if (rc) - return -EINTR; + if (!tty) + return 0; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; - if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + bs = ch->ch_bs; + if (!bs) return 0; - case TCSBRKP: - /* support for POSIX tcsendbreak() + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - * According to POSIX.1 spec (7.2.2.1.2) breaks should be - * between 0.25 and 0.5 seconds so we'll ask for something - * in the middle: 0.375 seconds. - */ - rc = tty_check_change(tty); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (rc) - return rc; + tmask = (ch->ch_tsize - 1); - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; + /* Get Transmit queue pointers */ + thead = readw(&(bs->tx_head)) & tmask; + ttail = readw(&(bs->tx_tail)) & tmask; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + /* Get tbusy flag */ + tbusy = readb(&(bs->tbusy)); - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + /* Get Command queue pointers */ + chead = readw(&(ch->ch_cm->cm_head)); + ctail = readw(&(ch->ch_cm->cm_tail)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return 0; + /* + * The only way we know for sure if there is no pending + * data left to be transferred, is if: + * 1) Transmit head and tail are equal (empty). + * 2) Command queue head and tail are equal (empty). + * 3) The "TBUSY" flag is 0. (Transmitter not busy). + */ - case TIOCSBRK: + if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) { + chars = 0; + } else { + if (thead >= ttail) + chars = thead - ttail; + else + chars = thead - ttail + ch->ch_tsize; /* - * FEP5 doesn't support turning on a break unconditionally. - * The FEP5 device will stop sending a break automatically - * after the specified time value that was sent when turning on - * the break. + * Fudge factor here. + * If chars is zero, we know that the command queue had + * something in it or tbusy was set. Because we cannot + * be sure if there is still some data to be transmitted, + * lets lie, and tell ld we have 1 byte left. */ - rc = tty_check_change(tty); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (rc) - return rc; - - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; + if (chars == 0) { + /* + * If TBUSY is still set, and our tx buffers are empty, + * force the firmware to send me another wakeup after + * TBUSY has been cleared. + */ + if (tbusy != 0) { + spin_lock_irqsave(&ch->ch_lock, lock_flags); + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + spin_unlock_irqrestore(&ch->ch_lock, + lock_flags); + } + chars = 1; + } + } - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + return chars; +} - dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); +static int dgap_wait_for_drain(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + struct bs_t __iomem *bs; + int ret = 0; + uint count = 1; + ulong lock_flags = 0; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (!tty || tty->magic != TTY_MAGIC) + return -EIO; - return 0; + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return -EIO; - case TIOCCBRK: - /* - * FEP5 doesn't support turning off a break unconditionally. - * The FEP5 device will stop sending a break automatically - * after the specified time value that was sent when turning on - * the break. - */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return -EIO; - case TIOCGSOFTCAR: + bs = ch->ch_bs; + if (!bs) + return -EIO; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + /* Loop until data is drained */ + while (count != 0) { - rc = put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long __user *) arg); - return rc; + count = dgap_tty_chars_in_buffer(tty); - case TIOCSSOFTCAR: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (count == 0) + break; - rc = get_user(arg, (unsigned long __user *) arg); - if (rc) - return rc; + /* Set flag waiting for drain */ + spin_lock_irqsave(&ch->ch_lock, lock_flags); + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - dgap_param(ch, bd, un->un_type); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + /* Go to sleep till we get woken up */ + ret = wait_event_interruptible(un->un_flags_wait, + ((un->un_flags & UN_EMPTY) == 0)); + /* If ret is non-zero, user ctrl-c'ed us */ + if (ret) + break; + } - return 0; + spin_lock_irqsave(&ch->ch_lock, lock_flags); + un->un_flags &= ~(UN_EMPTY); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - case TIOCMGET: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_get_modem_info(ch, uarg); + return ret; +} - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_set_modem_info(ch, bd, un, cmd, uarg); +/* + * dgap_maxcps_room + * + * Reduces bytes_available to the max number of characters + * that can be sent currently given the maxcps value, and + * returns the new bytes_available. This only affects printer + * output. + */ +static int dgap_maxcps_room(struct channel_t *ch, struct un_t *un, + int bytes_available) +{ + /* + * If its not the Transparent print device, return + * the full data amount. + */ + if (un->un_type != DGAP_PRINT) + return bytes_available; - /* - * Here are any additional ioctl's that we want to implement - */ + if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0) { + int cps_limit = 0; + unsigned long current_time = jiffies; + unsigned long buffer_time = current_time + + (HZ * ch->ch_digi.digi_bufsize) / + ch->ch_digi.digi_maxcps; - case TCFLSH: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - rc = tty_check_change(tty); - if (rc) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return rc; + if (ch->ch_cpstime < current_time) { + /* buffer is empty */ + ch->ch_cpstime = current_time; /* reset ch_cpstime */ + cps_limit = ch->ch_digi.digi_bufsize; + } else if (ch->ch_cpstime < buffer_time) { + /* still room in the buffer */ + cps_limit = ((buffer_time - ch->ch_cpstime) * + ch->ch_digi.digi_maxcps) / HZ; + } else { + /* no room in the buffer */ + cps_limit = 0; } - if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { - if (!(un->un_type == DGAP_PRINT)) { - head = readw(&(ch->ch_bs->rx_head)); - writew(head, &(ch->ch_bs->rx_tail)); - writeb(0, &(ch->ch_bs->orun)); - } - } + bytes_available = min(cps_limit, bytes_available); + } - if ((arg != TCOFLUSH) && (arg != TCIOFLUSH)) { - /* pretend we didn't recognize this IOCTL */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return bytes_available; +} - return -ENOIOCTLCMD; - } +static inline void dgap_set_firmware_event(struct un_t *un, unsigned int event) +{ + struct channel_t *ch; + struct bs_t __iomem *bs; + + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + bs = ch->ch_bs; + if (!bs) + return; - ch->ch_flags &= ~CH_STOP; - head = readw(&(ch->ch_bs->tx_head)); - dgap_cmdw(ch, FLUSHTX, (u16) head, 0); - dgap_cmdw(ch, RESUMETX, 0, 0); - if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); - wake_up_interruptible(&ch->ch_tun.un_flags_wait); + if ((event & UN_LOW) != 0) { + if ((un->un_flags & UN_LOW) == 0) { + un->un_flags |= UN_LOW; + writeb(1, &(bs->ilow)); } - if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { - ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); - wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + if ((event & UN_LOW) != 0) { + if ((un->un_flags & UN_EMPTY) == 0) { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); } - if (waitqueue_active(&tty->write_wait)) - wake_up_interruptible(&tty->write_wait); + } +} - /* Can't hold any locks when calling tty_wakeup! */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - tty_wakeup(tty); +/* + * dgap_tty_write_room() + * + * Return space available in Tx buffer + */ +static int dgap_tty_write_room(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + struct bs_t __iomem *bs; + u16 head, tail, tmask; + int ret; + ulong lock_flags = 0; - /* pretend we didn't recognize this IOCTL */ - return -ENOIOCTLCMD; + if (!tty) + return 0; - case TCSETSF: - case TCSETSW: - /* - * The linux tty driver doesn't have a flush - * input routine for the driver, assuming all backed - * up data is in the line disc. buffers. However, - * we all know that's not the case. Here, we - * act on the ioctl, but then lie and say we didn't - * so the line discipline will process the flush - * also. - */ - if (cmd == TCSETSF) { - /* flush rx */ - ch->ch_flags &= ~CH_STOP; - head = readw(&(ch->ch_bs->rx_head)); - writew(head, &(ch->ch_bs->rx_tail)); - } + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; - /* now wait for all the output to drain */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; - /* pretend we didn't recognize this */ - return -ENOIOCTLCMD; + bs = ch->ch_bs; + if (!bs) + return 0; - case TCSETAW: + spin_lock_irqsave(&ch->ch_lock, lock_flags); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; + tmask = ch->ch_tsize - 1; + head = readw(&(bs->tx_head)) & tmask; + tail = readw(&(bs->tx_tail)) & tmask; - /* pretend we didn't recognize this */ - return -ENOIOCTLCMD; + ret = tail - head - 1; + if (ret < 0) + ret += ch->ch_tsize; - case TCXONC: - /* - * The Linux Line Discipline (LD) would do this for us if we - * let it, but we have the special firmware options to do this - * the "right way" regardless of hardware or software flow - * control so we'll do it outselves instead of letting the LD - * do it. - */ - rc = tty_check_change(tty); - if (rc) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return rc; - } + /* Limit printer to maxcps */ + ret = dgap_maxcps_room(ch, un, ret); - switch (arg) { + /* + * If we are printer device, leave space for + * possibly both the on and off strings. + */ + if (un->un_type == DGAP_PRINT) { + if (!(ch->ch_flags & CH_PRON)) + ret -= ch->ch_digi.digi_onlen; + ret -= ch->ch_digi.digi_offlen; + } else { + if (ch->ch_flags & CH_PRON) + ret -= ch->ch_digi.digi_offlen; + } - case TCOON: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - dgap_tty_start(tty); - return 0; - case TCOOFF: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - dgap_tty_stop(tty); - return 0; - case TCION: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - /* Make the ld do it */ - return -ENOIOCTLCMD; - case TCIOFF: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - /* Make the ld do it */ - return -ENOIOCTLCMD; - default: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EINVAL; - } + if (ret < 0) + ret = 0; - case DIGI_GETA: - /* get information for ditty */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digigeta(ch, uarg); + /* + * Schedule FEP to wake us up if needed. + * + * TODO: This might be overkill... + * Do we really need to schedule callbacks from the FEP + * in every case? Can we get smarter based on ret? + */ + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - case DIGI_SETAW: - case DIGI_SETAF: + return ret; +} - /* set information for ditty */ - if (cmd == (DIGI_SETAW)) { +/* + * dgap_tty_write() + * + * Take data from the user or kernel and send it out to the FEP. + * In here exists all the Transparent Print magic as well. + */ +static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct channel_t *ch; + struct un_t *un; + struct bs_t __iomem *bs; + char __iomem *vaddr; + u16 head, tail, tmask, remain; + int bufcount, n; + ulong lock_flags; - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - rc = dgap_wait_for_drain(tty); - if (rc) - return -EINTR; - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - } else - tty_ldisc_flush(tty); - /* fall thru */ + if (!tty) + return 0; - case DIGI_SETA: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digiseta(ch, bd, un, uarg); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; - case DIGI_GEDELAY: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digigetedelay(tty, uarg); + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; - case DIGI_SEDELAY: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digisetedelay(ch, bd, un, uarg); + bs = ch->ch_bs; + if (!bs) + return 0; - case DIGI_GETCUSTOMBAUD: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digigetcustombaud(ch, un, uarg); + if (!count) + return 0; - case DIGI_SETCUSTOMBAUD: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return dgap_tty_digisetcustombaud(ch, bd, un, uarg); + spin_lock_irqsave(&ch->ch_lock, lock_flags); - case DIGI_RESET_PORT: - dgap_firmware_reset_port(ch); - dgap_param(ch, bd, un->un_type); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return 0; + /* Get our space available for the channel from the board */ + tmask = ch->ch_tsize - 1; + head = readw(&(bs->tx_head)) & tmask; + tail = readw(&(bs->tx_tail)) & tmask; - default: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + bufcount = tail - head - 1; + if (bufcount < 0) + bufcount += ch->ch_tsize; + + /* + * Limit printer output to maxcps overall, with bursts allowed + * up to bufsize characters. + */ + bufcount = dgap_maxcps_room(ch, un, bufcount); + + /* + * Take minimum of what the user wants to send, and the + * space available in the FEP buffer. + */ + count = min(count, bufcount); - return -ENOIOCTLCMD; + /* + * Bail if no space left. + */ + if (count <= 0) { + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + return 0; } -} -static int dgap_alloc_flipbuf(struct board_t *brd) -{ /* - * allocate flip buffer for board. + * Output the printer ON string, if we are in terminal mode, but + * need to be in printer mode. */ - brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); - if (!brd->flipbuf) - return -ENOMEM; - - brd->flipflagbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); - if (!brd->flipflagbuf) { - kfree(brd->flipbuf); - return -ENOMEM; + if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) { + dgap_wmove(ch, ch->ch_digi.digi_onstr, + (int) ch->ch_digi.digi_onlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags |= CH_PRON; } - return 0; -} + /* + * On the other hand, output the printer OFF string, if we are + * currently in printer mode, but need to output to the terminal. + */ + if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags &= ~CH_PRON; + } -static void dgap_free_flipbuf(struct board_t *brd) -{ - kfree(brd->flipbuf); - kfree(brd->flipflagbuf); -} + n = count; -/* - * Create pr and tty device entries - */ -static int dgap_tty_register_ports(struct board_t *brd) -{ - struct channel_t *ch; - int i; - int ret; + /* + * If the write wraps over the top of the circular buffer, + * move the portion up to the wrap point, and reset the + * pointers to the bottom. + */ + remain = ch->ch_tstart + ch->ch_tsize - head; - brd->serial_ports = kcalloc(brd->nasync, sizeof(*brd->serial_ports), - GFP_KERNEL); - if (!brd->serial_ports) - return -ENOMEM; + if (n >= remain) { + n -= remain; + vaddr = ch->ch_taddr + head; - brd->printer_ports = kcalloc(brd->nasync, sizeof(*brd->printer_ports), - GFP_KERNEL); - if (!brd->printer_ports) { - ret = -ENOMEM; - goto free_serial_ports; - } + memcpy_toio(vaddr, (u8 *) buf, remain); - for (i = 0; i < brd->nasync; i++) { - tty_port_init(&brd->serial_ports[i]); - tty_port_init(&brd->printer_ports[i]); + head = ch->ch_tstart; + buf += remain; } - ch = brd->channels[0]; - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { - - struct device *classp; - - classp = tty_port_register_device(&brd->serial_ports[i], - brd->serial_driver, - i, NULL); - - if (IS_ERR(classp)) { - ret = PTR_ERR(classp); - goto unregister_ttys; - } + if (n > 0) { - dgap_create_tty_sysfs(&ch->ch_tun, classp); - ch->ch_tun.un_sysfs = classp; + /* + * Move rest of data. + */ + vaddr = ch->ch_taddr + head; + remain = n; - classp = tty_port_register_device(&brd->printer_ports[i], - brd->print_driver, - i, NULL); + memcpy_toio(vaddr, (u8 *) buf, remain); + head += remain; - if (IS_ERR(classp)) { - ret = PTR_ERR(classp); - goto unregister_ttys; - } + } - dgap_create_tty_sysfs(&ch->ch_pun, classp); - ch->ch_pun.un_sysfs = classp; + if (count) { + ch->ch_txcount += count; + head &= tmask; + writew(head, &(bs->tx_head)); } - dgap_create_ports_sysfiles(brd); - return 0; + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); -unregister_ttys: - while (i >= 0) { - ch = brd->channels[i]; - if (ch->ch_tun.un_sysfs) { - dgap_remove_tty_sysfs(ch->ch_tun.un_sysfs); - tty_unregister_device(brd->serial_driver, i); - } + /* + * If this is the print device, and the + * printer is still on, we need to turn it + * off before going idle. If the buffer is + * non-empty, wait until it goes empty. + * Otherwise turn it off right now. + */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { + tail = readw(&(bs->tx_tail)) & tmask; - if (ch->ch_pun.un_sysfs) { - dgap_remove_tty_sysfs(ch->ch_pun.un_sysfs); - tty_unregister_device(brd->print_driver, i); + if (tail != head) { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } else { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags &= ~CH_PRON; } - i--; } - for (i = 0; i < brd->nasync; i++) { - tty_port_destroy(&brd->serial_ports[i]); - tty_port_destroy(&brd->printer_ports[i]); + /* Update printer buffer empty time. */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0) + && (ch->ch_digi.digi_bufsize > 0)) { + ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; } - kfree(brd->printer_ports); - brd->printer_ports = NULL; - -free_serial_ports: - kfree(brd->serial_ports); - brd->serial_ports = NULL; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - return ret; + return count; } /* - * Copies the BIOS code from the user to the board, - * and starts the BIOS running. + * dgap_tty_put_char() + * + * Put a character into ch->ch_buf + * + * - used by the line discipline for OPOST processing */ -static void dgap_do_bios_load(struct board_t *brd, const u8 *ubios, int len) +static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c) { - u8 __iomem *addr; - uint offset; - unsigned int i; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; - - addr = brd->re_map_membase; - - /* - * clear POST area - */ - for (i = 0; i < 16; i++) - writeb(0, addr + POSTAREA + i); - /* - * Download bios + * Simply call tty_write. */ - offset = 0x1000; - memcpy_toio(addr + offset, ubios, len); - - writel(0x0bf00401, addr); - writel(0, (addr + 4)); - - /* Clear the reset, and change states. */ - writeb(FEPCLR, brd->re_map_port); + dgap_tty_write(tty, &c, 1); + return 1; } /* - * Checks to see if the BIOS completed running on the card. + * Return modem signals to ld. */ -static int dgap_test_bios(struct board_t *brd) +static int dgap_tty_tiocmget(struct tty_struct *tty) { - u8 __iomem *addr; - u16 word; - u16 err1; - u16 err2; + struct channel_t *ch; + struct un_t *un; + int result; + u8 mstat; + ulong lock_flags; - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return -EINVAL; + if (!tty || tty->magic != TTY_MAGIC) + return -EIO; - addr = brd->re_map_membase; - word = readw(addr + POSTAREA); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return -EIO; - /* - * It can take 5-6 seconds for a board to - * pass the bios self test and post results. - * Give it 10 seconds. - */ - brd->wait_for_bios = 0; - while (brd->wait_for_bios < 1000) { - /* Check to see if BIOS thinks board is good. (GD). */ - if (word == *(u16 *) "GD") - return 0; - msleep_interruptible(10); - brd->wait_for_bios++; - word = readw(addr + POSTAREA); - } + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return -EIO; - /* Gave up on board after too long of time taken */ - err1 = readw(addr + SEQUENCE); - err2 = readw(addr + ERROR); - dev_warn(&brd->pdev->dev, "%s failed diagnostics. Error #(%x,%x).\n", - brd->name, err1, err2); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOBIOS; + spin_lock_irqsave(&ch->ch_lock, lock_flags); + + mstat = readb(&(ch->ch_bs->m_stat)); + /* Append any outbound signals that might be pending... */ + mstat |= ch->ch_mostat; + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + + result = 0; + + if (mstat & D_DTR(ch)) + result |= TIOCM_DTR; + if (mstat & D_RTS(ch)) + result |= TIOCM_RTS; + if (mstat & D_CTS(ch)) + result |= TIOCM_CTS; + if (mstat & D_DSR(ch)) + result |= TIOCM_DSR; + if (mstat & D_RI(ch)) + result |= TIOCM_RI; + if (mstat & D_CD(ch)) + result |= TIOCM_CD; - return -EIO; + return result; } /* - * Copies the FEP code from the user to the board, - * and starts the FEP running. + * dgap_tty_tiocmset() + * + * Set modem signals, called by ld. */ -static void dgap_do_fep_load(struct board_t *brd, const u8 *ufep, int len) +static int dgap_tty_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) { - u8 __iomem *addr; - uint offset; + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; + if (!tty || tty->magic != TTY_MAGIC) + return -EIO; - addr = brd->re_map_membase; + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return -EIO; - /* - * Download FEP - */ - offset = 0x1000; - memcpy_toio(addr + offset, ufep, len); + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return -EIO; - /* - * If board is a concentrator product, we need to give - * it its config string describing how the concentrators look. - */ - if ((brd->type == PCX) || (brd->type == PEPC)) { - u8 string[100]; - u8 __iomem *config; - u8 *xconfig; - unsigned int i = 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return -EIO; - xconfig = dgap_create_config_string(brd, string); + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - /* Write string to board memory */ - config = addr + CONFIG; - for (; i < CONFIGSIZE; i++, config++, xconfig++) { - writeb(*xconfig, config); - if ((*xconfig & 0xff) == 0xff) - break; - } + if (set & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval |= D_RTS(ch); } - writel(0xbfc01004, (addr + 0xc34)); - writel(0x3, (addr + 0xc30)); + if (set & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval |= D_DTR(ch); + } + + if (clear & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval &= ~(D_RTS(ch)); + } + + if (clear & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval &= ~(D_DTR(ch)); + } + + dgap_param(ch, bd, un->un_type); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return 0; } /* - * Waits for the FEP to report thats its ready for us to use. + * dgap_tty_send_break() + * + * Send a Break, called by ld. */ -static int dgap_test_fep(struct board_t *brd) +static int dgap_tty_send_break(struct tty_struct *tty, int msec) { - u8 __iomem *addr; - u16 word; - u16 err1; - u16 err2; + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return -EINVAL; + if (!tty || tty->magic != TTY_MAGIC) + return -EIO; - addr = brd->re_map_membase; - word = readw(addr + FEPSTAT); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return -EIO; - /* - * It can take 2-3 seconds for the FEP to - * be up and running. Give it 5 secs. - */ - brd->wait_for_fep = 0; - while (brd->wait_for_fep < 500) { - /* Check to see if FEP is up and running now. */ - if (word == *(u16 *) "OS") { - /* - * Check to see if the board can support FEP5+ commands. - */ - word = readw(addr + FEP5_PLUS); - if (word == *(u16 *) "5A") - brd->bd_flags |= BD_FEP5PLUS; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return -EIO; - return 0; - } - msleep_interruptible(10); - brd->wait_for_fep++; - word = readw(addr + FEPSTAT); + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return -EIO; + + switch (msec) { + case -1: + msec = 0xFFFF; + break; + case 0: + msec = 1; + break; + default: + msec /= 10; + break; } - /* Gave up on board after too long of time taken */ - err1 = readw(addr + SEQUENCE); - err2 = readw(addr + ERROR); - dev_warn(&brd->pdev->dev, - "FEPOS for %s not functioning. Error #(%x,%x).\n", - brd->name, err1, err2); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); +#if 0 + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); +#endif + dgap_cmdw(ch, SBREAK, (u16) msec, 0); - return -EIO; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return 0; } /* - * Physically forces the FEP5 card to reset itself. + * dgap_tty_wait_until_sent() + * + * wait until data has been transmitted, called by ld. */ -static void dgap_do_reset_board(struct board_t *brd) +static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout) { - u8 check; - u32 check1; - u32 check2; - unsigned int i; + dgap_wait_for_drain(tty); +} - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || - !brd->re_map_membase || !brd->re_map_port) +/* + * dgap_send_xchar() + * + * send a high priority character, called by ld. + */ +static void dgap_tty_send_xchar(struct tty_struct *tty, char c) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) return; - /* FEPRST does not vary among supported boards */ - writeb(FEPRST, brd->re_map_port); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; - for (i = 0; i <= 1000; i++) { - check = readb(brd->re_map_port) & 0xe; - if (check == FEPRST) - break; - udelay(10); + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - } - if (i > 1000) { - dev_warn(&brd->pdev->dev, - "dgap: Board not resetting... Failing board.\n"); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) return; - } + + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); /* - * Make sure there really is memory out there. + * This is technically what we should do. + * However, the NIST tests specifically want + * to see each XON or XOFF character that it + * sends, so lets just send each character + * by hand... */ - writel(0xa55a3cc3, (brd->re_map_membase + LOWMEM)); - writel(0x5aa5c33c, (brd->re_map_membase + HIGHMEM)); - check1 = readl(brd->re_map_membase + LOWMEM); - check2 = readl(brd->re_map_membase + HIGHMEM); +#if 0 + if (c == STOP_CHAR(tty)) + dgap_cmdw(ch, RPAUSE, 0, 0); + else if (c == START_CHAR(tty)) + dgap_cmdw(ch, RRESUME, 0, 0); + else + dgap_wmove(ch, &c, 1); +#else + dgap_wmove(ch, &c, 1); +#endif - if ((check1 != 0xa55a3cc3) || (check2 != 0x5aa5c33c)) { - dev_warn(&brd->pdev->dev, - "No memory at %p for board.\n", - brd->re_map_membase); - brd->state = BOARD_FAILED; - brd->dpastatus = BD_NOFEP; - return; - } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); } -#ifdef DIGI_CONCENTRATORS_SUPPORTED /* - * Sends a concentrator image into the FEP5 board. + * Return modem signals to ld. */ -static void dgap_do_conc_load(struct board_t *brd, u8 *uaddr, int len) +static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value) { - char __iomem *vaddr; - u16 offset; - struct downld_t *to_dp; + int result; + u8 mstat; + ulong lock_flags; + int rc; - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; + spin_lock_irqsave(&ch->ch_lock, lock_flags); - vaddr = brd->re_map_membase; + mstat = readb(&(ch->ch_bs->m_stat)); + /* Append any outbound signals that might be pending... */ + mstat |= ch->ch_mostat; - offset = readw((u16 *) (vaddr + DOWNREQ)); - to_dp = (struct downld_t *) (vaddr + (int) offset); - memcpy_toio(to_dp, uaddr, len); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - /* Tell card we have data for it */ - writew(0, vaddr + (DOWNREQ)); + result = 0; - brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; -} -#endif + if (mstat & D_DTR(ch)) + result |= TIOCM_DTR; + if (mstat & D_RTS(ch)) + result |= TIOCM_RTS; + if (mstat & D_CTS(ch)) + result |= TIOCM_CTS; + if (mstat & D_DSR(ch)) + result |= TIOCM_DSR; + if (mstat & D_RI(ch)) + result |= TIOCM_RI; + if (mstat & D_CD(ch)) + result |= TIOCM_CD; -#define EXPANSION_ROM_SIZE (64 * 1024) -#define FEP5_ROM_MAGIC (0xFEFFFFFF) + rc = put_user(result, value); -static void dgap_get_vpd(struct board_t *brd) -{ - u32 magic; - u32 base_offset; - u16 rom_offset; - u16 vpd_offset; - u16 image_length; - u16 i; - u8 byte1; - u8 byte2; + return rc; +} - /* - * Poke the magic number at the PCI Rom Address location. - * If VPD is supported, the value read from that address - * will be non-zero. - */ - magic = FEP5_ROM_MAGIC; - pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); - pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic); +/* + * dgap_set_modem_info() + * + * Set modem signals, called by ld. + */ +static int dgap_set_modem_info(struct channel_t *ch, struct board_t *bd, + struct un_t *un, unsigned int command, + unsigned int __user *value) +{ + int ret; + unsigned int arg; + ulong lock_flags; + ulong lock_flags2; - /* VPD not supported, bail */ - if (!magic) - return; + ret = get_user(arg, value); + if (ret) + return ret; - /* - * To get to the OTPROM memory, we have to send the boards base - * address or'ed with 1 into the PCI Rom Address location. - */ - magic = brd->membase | 0x01; - pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); - pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic); + switch (command) { + case TIOCMBIS: + if (arg & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval |= D_RTS(ch); + } - byte1 = readb(brd->re_map_membase); - byte2 = readb(brd->re_map_membase + 1); + if (arg & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval |= D_DTR(ch); + } - /* - * If the board correctly swapped to the OTPROM memory, - * the first 2 bytes (header) should be 0x55, 0xAA - */ - if (byte1 == 0x55 && byte2 == 0xAA) { + break; - base_offset = 0; + case TIOCMBIC: + if (arg & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval &= ~(D_RTS(ch)); + } - /* - * We have to run through all the OTPROM memory looking - * for the VPD offset. - */ - while (base_offset <= EXPANSION_ROM_SIZE) { + if (arg & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval &= ~(D_DTR(ch)); + } - /* - * Lots of magic numbers here. - * - * The VPD offset is located inside the ROM Data - * Structure. - * - * We also have to remember the length of each - * ROM Data Structure, so we can "hop" to the next - * entry if the VPD isn't in the current - * ROM Data Structure. - */ - rom_offset = readw(brd->re_map_membase + - base_offset + 0x18); - image_length = readw(brd->re_map_membase + - rom_offset + 0x10) * 512; - vpd_offset = readw(brd->re_map_membase + - rom_offset + 0x08); + break; - /* Found the VPD entry */ - if (vpd_offset) - break; + case TIOCMSET: + ch->ch_mforce = D_DTR(ch)|D_RTS(ch); - /* We didn't find a VPD entry, go to next ROM entry. */ - base_offset += image_length; + if (arg & TIOCM_RTS) + ch->ch_mval |= D_RTS(ch); + else + ch->ch_mval &= ~(D_RTS(ch)); - byte1 = readb(brd->re_map_membase + base_offset); - byte2 = readb(brd->re_map_membase + base_offset + 1); + if (arg & TIOCM_DTR) + ch->ch_mval |= (D_DTR(ch)); + else + ch->ch_mval &= ~(D_DTR(ch)); - /* - * If the new ROM offset doesn't have 0x55, 0xAA - * as its header, we have run out of ROM. - */ - if (byte1 != 0x55 || byte2 != 0xAA) - break; - } + break; - /* - * If we have a VPD offset, then mark the board - * as having a valid VPD, and copy VPDSIZE (512) bytes of - * that VPD to the buffer we have in our board structure. - */ - if (vpd_offset) { - brd->bd_flags |= BD_HAS_VPD; - for (i = 0; i < VPDSIZE; i++) { - brd->vpd[i] = readb(brd->re_map_membase + - vpd_offset + i); - } - } + default: + return -EINVAL; } - /* - * We MUST poke the magic number at the PCI Rom Address location again. - * This makes the card report the regular board memory back to us, - * rather than the OTPROM memory. - */ - magic = FEP5_ROM_MAGIC; - pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + + dgap_param(ch, bd, un->un_type); + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return 0; } /* - * Our board poller function. + * dgap_tty_digigeta() + * + * Ioctl to get the information for ditty. + * + * + * */ -static void dgap_poll_tasklet(unsigned long data) +static int dgap_tty_digigeta(struct channel_t *ch, + struct digi_t __user *retinfo) { - struct board_t *bd = (struct board_t *) data; + struct digi_t tmp; ulong lock_flags; - char __iomem *vaddr; - u16 head, tail; - if (!bd || (bd->magic != DGAP_BOARD_MAGIC)) - return; + if (!retinfo) + return -EFAULT; - if (bd->inhibit_poller) - return; + memset(&tmp, 0, sizeof(tmp)); - spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags); + memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - vaddr = bd->re_map_membase; + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; - /* - * If board is ready, parse deeper to see if there is anything to do. - */ - if (bd->state == BOARD_READY) { + return 0; +} - struct ev_t __iomem *eaddr; +/* + * dgap_tty_digiseta() + * + * Ioctl to set the information for ditty. + * + * + * + */ +static int dgap_tty_digiseta(struct channel_t *ch, struct board_t *bd, + struct un_t *un, struct digi_t __user *new_info) +{ + struct digi_t new_digi; + ulong lock_flags = 0; + unsigned long lock_flags2; - if (!bd->re_map_membase) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - if (!bd->re_map_port) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } + if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) + return -EFAULT; + + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - if (!bd->nasync) - goto out; + memcpy(&ch->ch_digi, &new_digi, sizeof(struct digi_t)); - eaddr = (struct ev_t __iomem *) (vaddr + EVBUF); + if (ch->ch_digi.digi_maxcps < 1) + ch->ch_digi.digi_maxcps = 1; - /* Get our head and tail */ - head = readw(&(eaddr->ev_head)); - tail = readw(&(eaddr->ev_tail)); + if (ch->ch_digi.digi_maxcps > 10000) + ch->ch_digi.digi_maxcps = 10000; - /* - * If there is an event pending. Go service it. - */ - if (head != tail) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - dgap_event(bd); - spin_lock_irqsave(&bd->bd_lock, lock_flags); - } + if (ch->ch_digi.digi_bufsize < 10) + ch->ch_digi.digi_bufsize = 10; -out: - /* - * If board is doing interrupts, ACK the interrupt. - */ - if (bd && bd->intr_running) - readb(bd->re_map_port + 2); + if (ch->ch_digi.digi_maxchar < 1) + ch->ch_digi.digi_maxchar = 1; - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } + if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) + ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; + + if (ch->ch_digi.digi_onlen > DIGI_PLEN) + ch->ch_digi.digi_onlen = DIGI_PLEN; + + if (ch->ch_digi.digi_offlen > DIGI_PLEN) + ch->ch_digi.digi_offlen = DIGI_PLEN; + dgap_param(ch, bd, un->un_type); + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return 0; } -/*======================================================================= +/* + * dgap_tty_digigetedelay() * - * dgap_cmdb - Sends a 2 byte command to the FEP. + * Ioctl to get the current edelay setting. * - * ch - Pointer to channel structure. - * cmd - Command to be sent. - * byte1 - Integer containing first byte to be sent. - * byte2 - Integer containing second byte to be sent. - * ncmds - Wait until ncmds or fewer cmds are left - * in the cmd buffer before returning. * - *=======================================================================*/ -static void dgap_cmdb(struct channel_t *ch, u8 cmd, u8 byte1, - u8 byte2, uint ncmds) + * + */ +static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo) { - char __iomem *vaddr; - struct __iomem cm_t *cm_addr; - uint count; - uint n; - u16 head; - u16 tail; + struct channel_t *ch; + struct un_t *un; + int tmp; + ulong lock_flags; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + if (!retinfo) + return -EFAULT; - /* - * Check if board is still alive. - */ - if (ch->ch_bd->state == BOARD_FAILED) - return; + if (!tty || tty->magic != TTY_MAGIC) + return -EFAULT; - /* - * Make sure the pointers are in range before - * writing to the FEP memory. - */ - vaddr = ch->ch_bd->re_map_membase; + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return -EFAULT; - if (!vaddr) - return; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return -EFAULT; - cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF); - head = readw(&(cm_addr->cm_head)); + memset(&tmp, 0, sizeof(tmp)); - /* - * Forget it if pointers out of range. - */ - if (head >= (CMDMAX - CMDSTART) || (head & 03)) { - ch->ch_bd->state = BOARD_FAILED; - return; - } + spin_lock_irqsave(&ch->ch_lock, lock_flags); + tmp = readw(&(ch->ch_bs->edelay)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - /* - * Put the data in the circular command buffer. - */ - writeb(cmd, (vaddr + head + CMDSTART + 0)); - writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1)); - writeb(byte1, (vaddr + head + CMDSTART + 2)); - writeb(byte2, (vaddr + head + CMDSTART + 3)); + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; - head = (head + 4) & (CMDMAX - CMDSTART - 4); + return 0; +} - writew(head, &(cm_addr->cm_head)); +/* + * dgap_tty_digisetedelay() + * + * Ioctl to set the EDELAY setting + * + */ +static int dgap_tty_digisetedelay(struct channel_t *ch, struct board_t *bd, + struct un_t *un, int __user *new_info) +{ + int new_digi; + ulong lock_flags; + ulong lock_flags2; - /* - * Wait if necessary before updating the head - * pointer to limit the number of outstanding - * commands to the FEP. If the time spent waiting - * is outlandish, declare the FEP dead. - */ - for (count = dgap_count ;;) { + if (copy_from_user(&new_digi, new_info, sizeof(int))) + return -EFAULT; - head = readw(&(cm_addr->cm_head)); - tail = readw(&(cm_addr->cm_tail)); + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - n = (head - tail) & (CMDMAX - CMDSTART - 4); + writew((u16) new_digi, &(ch->ch_bs->edelay)); - if (n <= ncmds * sizeof(struct cm_t)) - break; + dgap_param(ch, bd, un->un_type); - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return 0; } -/*======================================================================= - * - * dgap_cmdw - Sends a 1 word command to the FEP. +/* + * dgap_tty_digigetcustombaud() * - * ch - Pointer to channel structure. - * cmd - Command to be sent. - * word - Integer containing word to be sent. - * ncmds - Wait until ncmds or fewer cmds are left - * in the cmd buffer before returning. + * Ioctl to get the current custom baud rate setting. + */ +static int dgap_tty_digigetcustombaud(struct channel_t *ch, struct un_t *un, + int __user *retinfo) +{ + int tmp; + ulong lock_flags; + + if (!retinfo) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + + spin_lock_irqsave(&ch->ch_lock, lock_flags); + tmp = dgap_get_custom_baud(ch); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + + return 0; +} + +/* + * dgap_tty_digisetcustombaud() * - *=======================================================================*/ -static void dgap_cmdw(struct channel_t *ch, u8 cmd, u16 word, uint ncmds) + * Ioctl to set the custom baud rate setting + */ +static int dgap_tty_digisetcustombaud(struct channel_t *ch, struct board_t *bd, + struct un_t *un, int __user *new_info) { - char __iomem *vaddr; - struct __iomem cm_t *cm_addr; - uint count; - uint n; - u16 head; - u16 tail; + uint new_rate; + ulong lock_flags; + ulong lock_flags2; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + if (copy_from_user(&new_rate, new_info, sizeof(unsigned int))) + return -EFAULT; - /* - * Check if board is still alive. - */ - if (ch->ch_bd->state == BOARD_FAILED) - return; + if (bd->bd_flags & BD_FEP5PLUS) { - /* - * Make sure the pointers are in range before - * writing to the FEP memory. - */ - vaddr = ch->ch_bd->re_map_membase; - if (!vaddr) - return; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF); - head = readw(&(cm_addr->cm_head)); + ch->ch_custom_speed = new_rate; - /* - * Forget it if pointers out of range. - */ - if (head >= (CMDMAX - CMDSTART) || (head & 03)) { - ch->ch_bd->state = BOARD_FAILED; - return; + dgap_param(ch, bd, un->un_type); + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); } - /* - * Put the data in the circular command buffer. - */ - writeb(cmd, (vaddr + head + CMDSTART + 0)); - writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1)); - writew((u16) word, (vaddr + head + CMDSTART + 2)); + return 0; +} + +/* + * dgap_set_termios() + */ +static void dgap_tty_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + unsigned long lock_flags; + unsigned long lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; - head = (head + 4) & (CMDMAX - CMDSTART - 4); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; - writew(head, &(cm_addr->cm_head)); + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - /* - * Wait if necessary before updating the head - * pointer to limit the number of outstanding - * commands to the FEP. If the time spent waiting - * is outlandish, declare the FEP dead. - */ - for (count = dgap_count ;;) { + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; - head = readw(&(cm_addr->cm_head)); - tail = readw(&(cm_addr->cm_tail)); + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - n = (head - tail) & (CMDMAX - CMDSTART - 4); + ch->ch_c_cflag = tty->termios.c_cflag; + ch->ch_c_iflag = tty->termios.c_iflag; + ch->ch_c_oflag = tty->termios.c_oflag; + ch->ch_c_lflag = tty->termios.c_lflag; + ch->ch_startc = tty->termios.c_cc[VSTART]; + ch->ch_stopc = tty->termios.c_cc[VSTOP]; - if (n <= ncmds * sizeof(struct cm_t)) - break; + dgap_carrier(ch); + dgap_param(ch, bd, un->un_type); - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); } -/*======================================================================= - * - * dgap_cmdw_ext - Sends a extended word command to the FEP. - * - * ch - Pointer to channel structure. - * cmd - Command to be sent. - * word - Integer containing word to be sent. - * ncmds - Wait until ncmds or fewer cmds are left - * in the cmd buffer before returning. - * - *=======================================================================*/ -static void dgap_cmdw_ext(struct channel_t *ch, u16 cmd, u16 word, uint ncmds) +static void dgap_tty_throttle(struct tty_struct *tty) { - char __iomem *vaddr; - struct __iomem cm_t *cm_addr; - uint count; - uint n; - u16 head; - u16 tail; + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + if (!tty || tty->magic != TTY_MAGIC) return; - /* - * Check if board is still alive. - */ - if (ch->ch_bd->state == BOARD_FAILED) + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) return; - /* - * Make sure the pointers are in range before - * writing to the FEP memory. - */ - vaddr = ch->ch_bd->re_map_membase; - if (!vaddr) + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) return; - cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF); - head = readw(&(cm_addr->cm_head)); - - /* - * Forget it if pointers out of range. - */ - if (head >= (CMDMAX - CMDSTART) || (head & 03)) { - ch->ch_bd->state = BOARD_FAILED; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) return; - } - /* - * Put the data in the circular command buffer. - */ + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - /* Write an FF to tell the FEP that we want an extended command */ - writeb((u8) 0xff, (vaddr + head + CMDSTART + 0)); + ch->ch_flags |= (CH_RXBLOCK); +#if 1 + dgap_cmdw(ch, RPAUSE, 0, 0); +#endif - writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1)); - writew((u16) cmd, (vaddr + head + CMDSTART + 2)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - /* - * If the second part of the command won't fit, - * put it at the beginning of the circular buffer. - */ - if (((head + 4) >= ((CMDMAX - CMDSTART)) || (head & 03))) - writew((u16) word, (vaddr + CMDSTART)); - else - writew((u16) word, (vaddr + head + CMDSTART + 4)); +} - head = (head + 8) & (CMDMAX - CMDSTART - 4); +static void dgap_tty_unthrottle(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; - writew(head, &(cm_addr->cm_head)); + if (!tty || tty->magic != TTY_MAGIC) + return; - /* - * Wait if necessary before updating the head - * pointer to limit the number of outstanding - * commands to the FEP. If the time spent waiting - * is outlandish, declare the FEP dead. - */ - for (count = dgap_count ;;) { + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; - head = readw(&(cm_addr->cm_head)); - tail = readw(&(cm_addr->cm_tail)); + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - n = (head - tail) & (CMDMAX - CMDSTART - 4); + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; - if (n <= ncmds * sizeof(struct cm_t)) - break; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } + ch->ch_flags &= ~(CH_RXBLOCK); + +#if 1 + dgap_cmdw(ch, RRESUME, 0, 0); +#endif + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); } -/*======================================================================= +/************************************************************************ * - * dgap_wmove - Write data to FEP buffer. + * TTY Entry points and helper functions * - * ch - Pointer to channel structure. - * buf - Poiter to characters to be moved. - * cnt - Number of characters to move. + ************************************************************************/ + +/* + * dgap_tty_open() * - *=======================================================================*/ -static void dgap_wmove(struct channel_t *ch, char *buf, uint cnt) + */ +static int dgap_tty_open(struct tty_struct *tty, struct file *file) { - int n; - char __iomem *taddr; + struct board_t *brd; + struct channel_t *ch; + struct un_t *un; struct bs_t __iomem *bs; + uint major; + uint minor; + int rc; + ulong lock_flags; + ulong lock_flags2; u16 head; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + major = MAJOR(tty_devnum(tty)); + minor = MINOR(tty_devnum(tty)); + + if (major > 255) + return -EIO; + + /* Get board pointer from our array of majors we have allocated */ + brd = dgap_boards_by_major[major]; + if (!brd) + return -EIO; /* - * Check parameters. + * If board is not yet up to a state of READY, go to + * sleep waiting for it to happen or they cancel the open. */ - bs = ch->ch_bs; - head = readw(&(bs->tx_head)); + rc = wait_event_interruptible(brd->state_wait, + (brd->state & BOARD_READY)); + + if (rc) + return rc; + + spin_lock_irqsave(&brd->bd_lock, lock_flags); + + /* The wait above should guarantee this cannot happen */ + if (brd->state != BOARD_READY) { + spin_unlock_irqrestore(&brd->bd_lock, lock_flags); + return -EIO; + } + + /* If opened device is greater than our number of ports, bail. */ + if (MINOR(tty_devnum(tty)) > brd->nasync) { + spin_unlock_irqrestore(&brd->bd_lock, lock_flags); + return -EIO; + } + + ch = brd->channels[minor]; + if (!ch) { + spin_unlock_irqrestore(&brd->bd_lock, lock_flags); + return -EIO; + } + + /* Grab channel lock */ + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + + /* Figure out our type */ + if (major == brd->dgap_serial_major) { + un = &brd->channels[minor]->ch_tun; + un->un_type = DGAP_SERIAL; + } else if (major == brd->dgap_transparent_print_major) { + un = &brd->channels[minor]->ch_pun; + un->un_type = DGAP_PRINT; + } else { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&brd->bd_lock, lock_flags); + return -EIO; + } + + /* Store our unit into driver_data, so we always have it available. */ + tty->driver_data = un; /* - * If pointers are out of range, just return. + * Error if channel info pointer is NULL. */ - if ((cnt > ch->ch_tsize) || - (unsigned)(head - ch->ch_tstart) >= ch->ch_tsize) - return; + bs = ch->ch_bs; + if (!bs) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&brd->bd_lock, lock_flags); + return -EIO; + } /* - * If the write wraps over the top of the circular buffer, - * move the portion up to the wrap point, and reset the - * pointers to the bottom. + * Initialize tty's */ - n = ch->ch_tstart + ch->ch_tsize - head; + if (!(un->un_flags & UN_ISOPEN)) { + /* Store important variables. */ + un->un_tty = tty; - if (cnt >= n) { - cnt -= n; - taddr = ch->ch_taddr + head; - memcpy_toio(taddr, buf, n); - head = ch->ch_tstart; - buf += n; + /* Maybe do something here to the TTY struct as well? */ } /* - * Move rest of data. + * Initialize if neither terminal or printer is open. */ - taddr = ch->ch_taddr + head; - n = cnt; - memcpy_toio(taddr, buf, n); - head += cnt; - - writew(head, &(bs->tx_head)); -} + if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { -/* - * Retrives the current custom baud rate from FEP memory, - * and returns it back to the user. - * Returns 0 on error. - */ -static uint dgap_get_custom_baud(struct channel_t *ch) -{ - u8 __iomem *vaddr; - ulong offset; - uint value; + ch->ch_mforce = 0; + ch->ch_mval = 0; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; + /* + * Flush input queue. + */ + head = readw(&(bs->rx_head)); + writew(head, &(bs->rx_tail)); - if (!ch->ch_bd || ch->ch_bd->magic != DGAP_BOARD_MAGIC) - return 0; + ch->ch_flags = 0; + ch->pscan_state = 0; + ch->pscan_savechar = 0; - if (!(ch->ch_bd->bd_flags & BD_FEP5PLUS)) - return 0; + ch->ch_c_cflag = tty->termios.c_cflag; + ch->ch_c_iflag = tty->termios.c_iflag; + ch->ch_c_oflag = tty->termios.c_oflag; + ch->ch_c_lflag = tty->termios.c_lflag; + ch->ch_startc = tty->termios.c_cc[VSTART]; + ch->ch_stopc = tty->termios.c_cc[VSTOP]; - vaddr = ch->ch_bd->re_map_membase; + /* TODO: flush our TTY struct here? */ + } - if (!vaddr) - return 0; + dgap_carrier(ch); + /* + * Run param in case we changed anything + */ + dgap_param(ch, brd, un->un_type); /* - * Go get from fep mem, what the fep - * believes the custom baud rate is. + * follow protocol for opening port */ - offset = (ioread16(vaddr + ECS_SEG) << 4) + (ch->ch_portnum * 0x28) - + LINE_SPEED; - value = readw(vaddr + offset); - return value; -} + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&brd->bd_lock, lock_flags); -/* - * Calls the firmware to reset this channel. - */ -static void dgap_firmware_reset_port(struct channel_t *ch) -{ - dgap_cmdb(ch, CHRESET, 0, 0, 0); + rc = dgap_block_til_ready(tty, file, ch); - /* - * Now that the channel is reset, we need to make sure - * all the current settings get reapplied to the port - * in the firmware. - * - * So we will set the driver's cache of firmware - * settings all to 0, and then call param. - */ - ch->ch_fepiflag = 0; - ch->ch_fepcflag = 0; - ch->ch_fepoflag = 0; - ch->ch_fepstartc = 0; - ch->ch_fepstopc = 0; - ch->ch_fepastartc = 0; - ch->ch_fepastopc = 0; - ch->ch_mostat = 0; - ch->ch_hflow = 0; + if (!un->un_tty) + return -ENODEV; + + /* No going back now, increment our unit and channel counters */ + spin_lock_irqsave(&ch->ch_lock, lock_flags); + ch->ch_open_count++; + un->un_open_count++; + un->un_flags |= (UN_ISOPEN); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + + return rc; } -/*======================================================================= - * - * dgap_param - Set Digi parameters. - * - * struct tty_struct * - TTY for port. +/* + * dgap_tty_close() * - *=======================================================================*/ -static int dgap_param(struct channel_t *ch, struct board_t *bd, u32 un_type) + */ +static void dgap_tty_close(struct tty_struct *tty, struct file *file) { - u16 head; - u16 cflag; - u16 iflag; - u8 mval; - u8 hflow; - - /* - * If baud rate is zero, flush queues, and set mval to drop DTR. - */ - if ((ch->ch_c_cflag & (CBAUD)) == 0) { + struct ktermios *ts; + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; - /* flush rx */ - head = readw(&(ch->ch_bs->rx_head)); - writew(head, &(ch->ch_bs->rx_tail)); + if (!tty || tty->magic != TTY_MAGIC) + return; - /* flush tx */ - head = readw(&(ch->ch_bs->tx_head)); - writew(head, &(ch->ch_bs->tx_tail)); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; - ch->ch_flags |= (CH_BAUD0); + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - /* Drop RTS and DTR */ - ch->ch_mval &= ~(D_RTS(ch)|D_DTR(ch)); - mval = D_DTR(ch) | D_RTS(ch); - ch->ch_baud_info = 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; - } else if (ch->ch_custom_speed && (bd->bd_flags & BD_FEP5PLUS)) { - /* - * Tell the fep to do the command - */ + ts = &tty->termios; - dgap_cmdw_ext(ch, 0xff01, ch->ch_custom_speed, 0); + spin_lock_irqsave(&ch->ch_lock, lock_flags); + /* + * Determine if this is the last close or not - and if we agree about + * which type of close it is with the Line Discipline + */ + if ((tty->count == 1) && (un->un_open_count != 1)) { /* - * Now go get from fep mem, what the fep - * believes the custom baud rate is. + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. un_open_count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. */ - ch->ch_custom_speed = dgap_get_custom_baud(ch); - ch->ch_baud_info = ch->ch_custom_speed; + un->un_open_count = 1; + } - /* Handle transition from B0 */ - if (ch->ch_flags & CH_BAUD0) { - ch->ch_flags &= ~(CH_BAUD0); - ch->ch_mval |= (D_RTS(ch)|D_DTR(ch)); - } - mval = D_DTR(ch) | D_RTS(ch); + if (--un->un_open_count < 0) + un->un_open_count = 0; - } else { - /* - * Set baud rate, character size, and parity. - */ + ch->ch_open_count--; + if (ch->ch_open_count && un->un_open_count) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + return; + } - int iindex = 0; - int jindex = 0; - int baud = 0; + /* OK, its the last close on the unit */ - ulong bauds[4][16] = { - { /* slowbaud */ - 0, 50, 75, 110, - 134, 150, 200, 300, - 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 }, - { /* slowbaud & CBAUDEX */ - 0, 57600, 115200, 230400, - 460800, 150, 200, 921600, - 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 }, - { /* fastbaud */ - 0, 57600, 76800, 115200, - 14400, 57600, 230400, 76800, - 115200, 230400, 28800, 460800, - 921600, 9600, 19200, 38400 }, - { /* fastbaud & CBAUDEX */ - 0, 57600, 115200, 230400, - 460800, 150, 200, 921600, - 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 } - }; + un->un_flags |= UN_CLOSING; - /* - * Only use the TXPrint baud rate if the - * terminal unit is NOT open - */ - if (!(ch->ch_tun.un_flags & UN_ISOPEN) && - un_type == DGAP_PRINT) - baud = C_BAUD(ch->ch_pun.un_tty) & 0xff; - else - baud = C_BAUD(ch->ch_tun.un_tty) & 0xff; + tty->closing = 1; + + /* + * Only officially close channel if count is 0 and + * DIGI_PRINTER bit is not set. + */ + if ((ch->ch_open_count == 0) && + !(ch->ch_digi.digi_flags & DIGI_PRINTER)) { - if (ch->ch_c_cflag & CBAUDEX) - iindex = 1; + ch->ch_flags &= ~(CH_RXBLOCK); - if (ch->ch_digi.digi_flags & DIGI_FAST) - iindex += 2; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jindex = baud; + /* wait for output to drain */ + /* This will also return if we take an interrupt */ - if ((iindex >= 0) && (iindex < 4) && - (jindex >= 0) && (jindex < 16)) - baud = bauds[iindex][jindex]; - else - baud = 0; + dgap_wait_for_drain(tty); - if (baud == 0) - baud = 9600; + dgap_tty_flush_buffer(tty); + tty_ldisc_flush(tty); - ch->ch_baud_info = baud; + spin_lock_irqsave(&ch->ch_lock, lock_flags); - /* - * CBAUD has bit position 0x1000 set these days to - * indicate Linux baud rate remap. - * We use a different bit assignment for high speed. - * Clear this bit out while grabbing the parts of - * "cflag" we want. - */ - cflag = ch->ch_c_cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | - CSTOPB | CSIZE); + tty->closing = 0; /* - * HUPCL bit is used by FEP to indicate fast baud - * table is to be used. + * If we have HUPCL set, lower DTR and RTS */ - if ((ch->ch_digi.digi_flags & DIGI_FAST) || - (ch->ch_c_cflag & CBAUDEX)) - cflag |= HUPCL; + if (ch->ch_c_cflag & HUPCL) { + ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch)); + dgap_cmdb(ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0); - if ((ch->ch_c_cflag & CBAUDEX) && - !(ch->ch_digi.digi_flags & DIGI_FAST)) { /* - * The below code is trying to guarantee that only - * baud rates 115200, 230400, 460800, 921600 are - * remapped. We use exclusive or because the various - * baud rates share common bit positions and therefore - * can't be tested for easily. + * Go to sleep to ensure RTS/DTR + * have been dropped for modems to see it. */ - tcflag_t tcflag = (ch->ch_c_cflag & CBAUD) | CBAUDEX; - int baudpart = 0; + spin_unlock_irqrestore(&ch->ch_lock, + lock_flags); - /* - * Map high speed requests to index - * into FEP's baud table - */ - switch (tcflag) { - case B57600: - baudpart = 1; - break; -#ifdef B76800 - case B76800: - baudpart = 2; - break; -#endif - case B115200: - baudpart = 3; - break; - case B230400: - baudpart = 9; - break; - case B460800: - baudpart = 11; - break; -#ifdef B921600 - case B921600: - baudpart = 12; - break; -#endif - default: - baudpart = 0; - } + /* .25 second delay for dropping RTS/DTR */ + schedule_timeout_interruptible(msecs_to_jiffies(250)); - if (baudpart) - cflag = (cflag & ~(CBAUD | CBAUDEX)) | baudpart; + spin_lock_irqsave(&ch->ch_lock, lock_flags); } - cflag &= 0xffff; - - if (cflag != ch->ch_fepcflag) { - ch->ch_fepcflag = (u16) (cflag & 0xffff); - - /* - * Okay to have channel and board - * locks held calling this - */ - dgap_cmdw(ch, SCFLAG, (u16) cflag, 0); - } + ch->pscan_state = 0; + ch->pscan_savechar = 0; + ch->ch_baud_info = 0; - /* Handle transition from B0 */ - if (ch->ch_flags & CH_BAUD0) { - ch->ch_flags &= ~(CH_BAUD0); - ch->ch_mval |= (D_RTS(ch)|D_DTR(ch)); - } - mval = D_DTR(ch) | D_RTS(ch); } /* - * Get input flags. + * turn off print device when closing print device. */ - iflag = ch->ch_c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | - INPCK | ISTRIP | IXON | IXANY | IXOFF); - - if ((ch->ch_startc == _POSIX_VDISABLE) || - (ch->ch_stopc == _POSIX_VDISABLE)) { - iflag &= ~(IXON | IXOFF); - ch->ch_c_iflag &= ~(IXON | IXOFF); + if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + ch->ch_flags &= ~CH_PRON; } - /* - * Only the IBM Xr card can switch between - * 232 and 422 modes on the fly - */ - if (bd->device == PCI_DEV_XR_IBM_DID) { - if (ch->ch_digi.digi_flags & DIGI_422) - dgap_cmdb(ch, SCOMMODE, MODE_422, 0, 0); - else - dgap_cmdb(ch, SCOMMODE, MODE_232, 0, 0); - } + un->un_tty = NULL; + un->un_flags &= ~(UN_ISOPEN | UN_CLOSING); + tty->driver_data = NULL; - if (ch->ch_digi.digi_flags & DIGI_ALTPIN) - iflag |= IALTPIN; + wake_up_interruptible(&ch->ch_flags_wait); + wake_up_interruptible(&un->un_flags_wait); - if (iflag != ch->ch_fepiflag) { - ch->ch_fepiflag = iflag; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); +} - /* Okay to have channel and board locks held calling this */ - dgap_cmdw(ch, SIFLAG, (u16) ch->ch_fepiflag, 0); - } +static void dgap_tty_start(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; - /* - * Select hardware handshaking. - */ - hflow = 0; + if (!tty || tty->magic != TTY_MAGIC) + return; - if (ch->ch_c_cflag & CRTSCTS) - hflow |= (D_RTS(ch) | D_CTS(ch)); - if (ch->ch_digi.digi_flags & RTSPACE) - hflow |= D_RTS(ch); - if (ch->ch_digi.digi_flags & DTRPACE) - hflow |= D_DTR(ch); - if (ch->ch_digi.digi_flags & CTSPACE) - hflow |= D_CTS(ch); - if (ch->ch_digi.digi_flags & DSRPACE) - hflow |= D_DSR(ch); - if (ch->ch_digi.digi_flags & DCDPACE) - hflow |= D_CD(ch); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; - if (hflow != ch->ch_hflow) { - ch->ch_hflow = hflow; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SHFLOW, (u8) hflow, 0xff, 0); - } + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; - /* - * Set RTS and/or DTR Toggle if needed, - * but only if product is FEP5+ based. - */ - if (bd->bd_flags & BD_FEP5PLUS) { - u16 hflow2 = 0; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) - hflow2 |= (D_RTS(ch)); - if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) - hflow2 |= (D_DTR(ch)); + dgap_cmdw(ch, RESUMETX, 0, 0); - dgap_cmdw_ext(ch, 0xff03, hflow2, 0); - } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); +} - /* - * Set modem control lines. - */ +static void dgap_tty_stop(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; - mval ^= ch->ch_mforce & (mval ^ ch->ch_mval); + if (!tty || tty->magic != TTY_MAGIC) + return; - if (ch->ch_mostat ^ mval) { - ch->ch_mostat = mval; + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, PAUSETX, 0, 0); + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); +} + +/* + * dgap_tty_flush_chars() + * + * Flush the cook buffer + * + * Note to self, and any other poor souls who venture here: + * + * flush in this case DOES NOT mean dispose of the data. + * instead, it means "stop buffering and send it if you + * haven't already." Just guess how I figured that out... SRW 2-Jun-98 + * + * It is also always called in interrupt context - JAR 8-Sept-99 + */ +static void dgap_tty_flush_chars(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + ulong lock_flags2; - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SMODEM, (u8) mval, D_RTS(ch)|D_DTR(ch), 0); - } + if (!tty || tty->magic != TTY_MAGIC) + return; - /* - * Read modem signals, and then call carrier function. - */ - ch->ch_mistat = readb(&(ch->ch_bs->m_stat)); - dgap_carrier(ch); + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; - /* - * Set the start and stop characters. - */ - if (ch->ch_startc != ch->ch_fepstartc || - ch->ch_stopc != ch->ch_fepstopc) { - ch->ch_fepstartc = ch->ch_startc; - ch->ch_fepstopc = ch->ch_stopc; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SFLOWC, ch->ch_fepstartc, ch->ch_fepstopc, 0); - } + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; - /* - * Set the Auxiliary start and stop characters. - */ - if (ch->ch_astartc != ch->ch_fepastartc || - ch->ch_astopc != ch->ch_fepastopc) { - ch->ch_fepastartc = ch->ch_astartc; - ch->ch_fepastopc = ch->ch_astopc; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SAFLOWC, ch->ch_fepastartc, ch->ch_fepastopc, 0); - } + /* TODO: Do something here */ - return 0; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); } +/***************************************************************************** + * + * The IOCTL function and all of its helpers + * + *****************************************************************************/ + /* - * dgap_parity_scan() + * dgap_tty_ioctl() * - * Convert the FEP5 way of reporting parity errors and breaks into - * the Linux line discipline way. + * The usual assortment of ioctl's */ -static void dgap_parity_scan(struct channel_t *ch, unsigned char *cbuf, - unsigned char *fbuf, int *len) +static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) { - int l = *len; - int count = 0; - unsigned char *in, *cout, *fout; - unsigned char c; + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int rc; + u16 head; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + void __user *uarg = (void __user *) arg; - in = cbuf; - cout = cbuf; - fout = fbuf; + if (!tty || tty->magic != TTY_MAGIC) + return -ENODEV; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return -ENODEV; + ch = un->un_ch; if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; + return -ENODEV; - while (l--) { - c = *in++; - switch (ch->pscan_state) { - default: - /* reset to sanity and fall through */ - ch->pscan_state = 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return -ENODEV; - case 0: - /* No FF seen yet */ - if (c == (unsigned char) '\377') - /* delete this character from stream */ - ch->pscan_state = 1; - else { - *cout++ = c; - *fout++ = TTY_NORMAL; - count += 1; - } - break; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - case 1: - /* first FF seen */ - if (c == (unsigned char) '\377') { - /* doubled ff, transform to single ff */ - *cout++ = c; - *fout++ = TTY_NORMAL; - count += 1; - ch->pscan_state = 0; - } else { - /* save value examination in next state */ - ch->pscan_savechar = c; - ch->pscan_state = 2; - } - break; + if (un->un_open_count <= 0) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return -EIO; + } - case 2: - /* third character of ff sequence */ + switch (cmd) { - *cout++ = c; + /* Here are all the standard ioctl's that we MUST implement */ - if (ch->pscan_savechar == 0x0) { + case TCSBRK: + /* + * TCSBRK is SVID version: non-zero arg --> no break + * this behaviour is exploited by tcdrain(). + * + * According to POSIX.1 spec (7.2.2.1.2) breaks should be + * between 0.25 and 0.5 seconds so we'll ask for something + * in the middle: 0.375 seconds. + */ + rc = tty_check_change(tty); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (rc) + return rc; - if (c == 0x0) { - ch->ch_err_break++; - *fout++ = TTY_BREAK; - } else { - ch->ch_err_parity++; - *fout++ = TTY_PARITY; - } - } + rc = dgap_wait_for_drain(tty); - count += 1; - ch->pscan_state = 0; - } - } - *len = count; -} + if (rc) + return -EINTR; -static void dgap_write_wakeup(struct board_t *bd, struct channel_t *ch, - struct un_t *un, u32 mask, - unsigned long *irq_flags1, - unsigned long *irq_flags2) -{ - if (!(un->un_flags & mask)) - return; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - un->un_flags &= ~mask; + if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); - if (!(un->un_flags & UN_ISOPEN)) - return; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if ((un->un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - un->un_tty->ldisc->ops->write_wakeup) { - spin_unlock_irqrestore(&ch->ch_lock, *irq_flags2); - spin_unlock_irqrestore(&bd->bd_lock, *irq_flags1); + return 0; - (un->un_tty->ldisc->ops->write_wakeup)(un->un_tty); + case TCSBRKP: + /* support for POSIX tcsendbreak() - spin_lock_irqsave(&bd->bd_lock, *irq_flags1); - spin_lock_irqsave(&ch->ch_lock, *irq_flags2); - } - wake_up_interruptible(&un->un_tty->write_wait); - wake_up_interruptible(&un->un_flags_wait); -} + * According to POSIX.1 spec (7.2.2.1.2) breaks should be + * between 0.25 and 0.5 seconds so we'll ask for something + * in the middle: 0.375 seconds. + */ + rc = tty_check_change(tty); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (rc) + return rc; -/*======================================================================= - * - * dgap_event - FEP to host event processing routine. - * - * bd - Board of current event. - * - *=======================================================================*/ -static int dgap_event(struct board_t *bd) -{ - struct channel_t *ch; - ulong lock_flags; - ulong lock_flags2; - struct bs_t __iomem *bs; - u8 __iomem *event; - u8 __iomem *vaddr; - struct ev_t __iomem *eaddr; - uint head; - uint tail; - int port; - int reason; - int modem; - int b1; + rc = dgap_wait_for_drain(tty); + if (rc) + return -EINTR; + + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return 0; + + case TIOCSBRK: + /* + * FEP5 doesn't support turning on a break unconditionally. + * The FEP5 device will stop sending a break automatically + * after the specified time value that was sent when turning on + * the break. + */ + rc = tty_check_change(tty); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + if (rc) + return rc; + + rc = dgap_wait_for_drain(tty); + if (rc) + return -EINTR; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -EIO; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); - spin_lock_irqsave(&bd->bd_lock, lock_flags); + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); - vaddr = bd->re_map_membase; + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (!vaddr) { + return 0; + + case TIOCCBRK: + /* + * FEP5 doesn't support turning off a break unconditionally. + * The FEP5 device will stop sending a break automatically + * after the specified time value that was sent when turning on + * the break. + */ + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EIO; - } + return 0; - eaddr = (struct ev_t __iomem *) (vaddr + EVBUF); + case TIOCGSOFTCAR: - /* Get our head and tail */ - head = readw(&(eaddr->ev_head)); - tail = readw(&(eaddr->ev_tail)); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - /* - * Forget it if pointers out of range. - */ + rc = put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long __user *) arg); + return rc; - if (head >= EVMAX - EVSTART || tail >= EVMAX - EVSTART || - (head | tail) & 03) { - /* Let go of board lock */ + case TIOCSSOFTCAR: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EIO; - } - /* - * Loop to process all the events in the buffer. - */ - while (tail != head) { + rc = get_user(arg, (unsigned long __user *) arg); + if (rc) + return rc; + + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + dgap_param(ch, bd, un->un_type); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return 0; + + case TIOCMGET: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_get_modem_info(ch, uarg); + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_set_modem_info(ch, bd, un, cmd, uarg); /* - * Get interrupt information. + * Here are any additional ioctl's that we want to implement */ - event = bd->re_map_membase + tail + EVSTART; + case TCFLSH: + /* + * The linux tty driver doesn't have a flush + * input routine for the driver, assuming all backed + * up data is in the line disc. buffers. However, + * we all know that's not the case. Here, we + * act on the ioctl, but then lie and say we didn't + * so the line discipline will process the flush + * also. + */ + rc = tty_check_change(tty); + if (rc) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return rc; + } - port = ioread8(event); - reason = ioread8(event + 1); - modem = ioread8(event + 2); - b1 = ioread8(event + 3); + if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { + if (!(un->un_type == DGAP_PRINT)) { + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); + writeb(0, &(ch->ch_bs->orun)); + } + } + + if ((arg != TCOFLUSH) && (arg != TCIOFLUSH)) { + /* pretend we didn't recognize this IOCTL */ + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + + return -ENOIOCTLCMD; + } + + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->tx_head)); + dgap_cmdw(ch, FLUSHTX, (u16) head, 0); + dgap_cmdw(ch, RESUMETX, 0, 0); + if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + if (waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + /* Can't hold any locks when calling tty_wakeup! */ + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + tty_wakeup(tty); + + /* pretend we didn't recognize this IOCTL */ + return -ENOIOCTLCMD; + + case TCSETSF: + case TCSETSW: /* - * Make sure the interrupt is valid. + * The linux tty driver doesn't have a flush + * input routine for the driver, assuming all backed + * up data is in the line disc. buffers. However, + * we all know that's not the case. Here, we + * act on the ioctl, but then lie and say we didn't + * so the line discipline will process the flush + * also. */ - if (port >= bd->nasync) - goto next; + if (cmd == TCSETSF) { + /* flush rx */ + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); + } - if (!(reason & (IFMODEM | IFBREAK | IFTLW | IFTEM | IFDATA))) - goto next; + /* now wait for all the output to drain */ + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) + return -EINTR; - ch = bd->channels[port]; + /* pretend we didn't recognize this */ + return -ENOIOCTLCMD; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - goto next; + case TCSETAW: + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) + return -EINTR; + + /* pretend we didn't recognize this */ + return -ENOIOCTLCMD; + case TCXONC: /* - * If we have made it here, the event was valid. - * Lock down the channel. + * The Linux Line Discipline (LD) would do this for us if we + * let it, but we have the special firmware options to do this + * the "right way" regardless of hardware or software flow + * control so we'll do it outselves instead of letting the LD + * do it. */ - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + rc = tty_check_change(tty); + if (rc) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return rc; + } - bs = ch->ch_bs; + switch (arg) { - if (!bs) { + case TCOON: spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - goto next; + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + dgap_tty_start(tty); + return 0; + case TCOOFF: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + dgap_tty_stop(tty); + return 0; + case TCION: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + /* Make the ld do it */ + return -ENOIOCTLCMD; + case TCIOFF: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + /* Make the ld do it */ + return -ENOIOCTLCMD; + default: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return -EINVAL; } - /* - * Process received data. - */ - if (reason & IFDATA) { + case DIGI_GETA: + /* get information for ditty */ + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_tty_digigeta(ch, uarg); + + case DIGI_SETAW: + case DIGI_SETAF: + + /* set information for ditty */ + if (cmd == (DIGI_SETAW)) { + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) + return -EINTR; + spin_lock_irqsave(&bd->bd_lock, lock_flags); + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + } else + tty_ldisc_flush(tty); + /* fall thru */ + + case DIGI_SETA: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_tty_digiseta(ch, bd, un, uarg); + + case DIGI_GEDELAY: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_tty_digigetedelay(tty, uarg); + + case DIGI_SEDELAY: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_tty_digisetedelay(ch, bd, un, uarg); - /* - * ALL LOCKS *MUST* BE DROPPED BEFORE CALLING INPUT! - * input could send some data to ld, which in turn - * could do a callback to one of our other functions. - */ - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + case DIGI_GETCUSTOMBAUD: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_tty_digigetcustombaud(ch, un, uarg); - dgap_input(ch); + case DIGI_SETCUSTOMBAUD: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return dgap_tty_digisetcustombaud(ch, bd, un, uarg); - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); + case DIGI_RESET_PORT: + dgap_firmware_reset_port(ch); + dgap_param(ch, bd, un->un_type); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + return 0; - if (ch->ch_flags & CH_RACTIVE) - ch->ch_flags |= CH_RENABLE; - else - writeb(1, &(bs->idata)); + default: + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - if (ch->ch_flags & CH_RWAIT) { - ch->ch_flags &= ~CH_RWAIT; + return -ENOIOCTLCMD; + } +} - wake_up_interruptible - (&ch->ch_tun.un_flags_wait); - } - } +static const struct tty_operations dgap_tty_ops = { + .open = dgap_tty_open, + .close = dgap_tty_close, + .write = dgap_tty_write, + .write_room = dgap_tty_write_room, + .flush_buffer = dgap_tty_flush_buffer, + .chars_in_buffer = dgap_tty_chars_in_buffer, + .flush_chars = dgap_tty_flush_chars, + .ioctl = dgap_tty_ioctl, + .set_termios = dgap_tty_set_termios, + .stop = dgap_tty_stop, + .start = dgap_tty_start, + .throttle = dgap_tty_throttle, + .unthrottle = dgap_tty_unthrottle, + .hangup = dgap_tty_hangup, + .put_char = dgap_tty_put_char, + .tiocmget = dgap_tty_tiocmget, + .tiocmset = dgap_tty_tiocmset, + .break_ctl = dgap_tty_send_break, + .wait_until_sent = dgap_tty_wait_until_sent, + .send_xchar = dgap_tty_send_xchar +}; - /* - * Process Modem change signals. - */ - if (reason & IFMODEM) { - ch->ch_mistat = modem; - dgap_carrier(ch); - } +/************************************************************************ + * + * TTY Initialization/Cleanup Functions + * + ************************************************************************/ - /* - * Process break. - */ - if (reason & IFBREAK) { +/* + * dgap_tty_register() + * + * Init the tty subsystem for this board. + */ +static int dgap_tty_register(struct board_t *brd) +{ + int rc; - if (ch->ch_tun.un_tty) { - /* A break has been indicated */ - ch->ch_err_break++; - tty_buffer_request_room - (ch->ch_tun.un_tty->port, 1); - tty_insert_flip_char(ch->ch_tun.un_tty->port, - 0, TTY_BREAK); - tty_flip_buffer_push(ch->ch_tun.un_tty->port); - } - } + brd->serial_driver = tty_alloc_driver(MAXPORTS, 0); + if (IS_ERR(brd->serial_driver)) + return PTR_ERR(brd->serial_driver); - /* - * Process Transmit low. - */ - if (reason & IFTLW) { - dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_LOW, - &lock_flags, &lock_flags2); - dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_LOW, - &lock_flags, &lock_flags2); - if (ch->ch_flags & CH_WLOW) { - ch->ch_flags &= ~CH_WLOW; - wake_up_interruptible(&ch->ch_flags_wait); - } - } + snprintf(brd->serial_name, MAXTTYNAMELEN, "tty_dgap_%d_", + brd->boardnum); + brd->serial_driver->name = brd->serial_name; + brd->serial_driver->name_base = 0; + brd->serial_driver->major = 0; + brd->serial_driver->minor_start = 0; + brd->serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + brd->serial_driver->subtype = SERIAL_TYPE_NORMAL; + brd->serial_driver->init_termios = dgap_default_termios; + brd->serial_driver->driver_name = DRVSTR; + brd->serial_driver->flags = (TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_HARDWARE_BREAK); - /* - * Process Transmit empty. - */ - if (reason & IFTEM) { - dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_EMPTY, - &lock_flags, &lock_flags2); - dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_EMPTY, - &lock_flags, &lock_flags2); - if (ch->ch_flags & CH_WEMPTY) { - ch->ch_flags &= ~CH_WEMPTY; - wake_up_interruptible(&ch->ch_flags_wait); - } - } + /* The kernel wants space to store pointers to tty_structs */ + brd->serial_driver->ttys = + kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->serial_driver->ttys) { + rc = -ENOMEM; + goto free_serial_drv; + } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + tty_set_operations(brd->serial_driver, &dgap_tty_ops); -next: - tail = (tail + 4) & (EVMAX - EVSTART - 4); + /* + * If we're doing transparent print, we have to do all of the above + * again, separately so we don't get the LD confused about what major + * we are when we get into the dgap_tty_open() routine. + */ + brd->print_driver = tty_alloc_driver(MAXPORTS, 0); + if (IS_ERR(brd->print_driver)) { + rc = PTR_ERR(brd->print_driver); + goto free_serial_drv; } - writew(tail, &(eaddr->ev_tail)); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); + snprintf(brd->print_name, MAXTTYNAMELEN, "pr_dgap_%d_", + brd->boardnum); + brd->print_driver->name = brd->print_name; + brd->print_driver->name_base = 0; + brd->print_driver->major = 0; + brd->print_driver->minor_start = 0; + brd->print_driver->type = TTY_DRIVER_TYPE_SERIAL; + brd->print_driver->subtype = SERIAL_TYPE_NORMAL; + brd->print_driver->init_termios = dgap_default_termios; + brd->print_driver->driver_name = DRVSTR; + brd->print_driver->flags = (TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_HARDWARE_BREAK); - return 0; -} + /* The kernel wants space to store pointers to tty_structs */ + brd->print_driver->ttys = + kzalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->print_driver->ttys) { + rc = -ENOMEM; + goto free_print_drv; + } -static ssize_t dgap_driver_version_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", DG_PART); -} -static DRIVER_ATTR(version, S_IRUSR, dgap_driver_version_show, NULL); + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + tty_set_operations(brd->print_driver, &dgap_tty_ops); + /* Register tty devices */ + rc = tty_register_driver(brd->serial_driver); + if (rc < 0) + goto free_print_drv; -static ssize_t dgap_driver_boards_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", dgap_numboards); -} -static DRIVER_ATTR(boards, S_IRUSR, dgap_driver_boards_show, NULL); + /* Register Transparent Print devices */ + rc = tty_register_driver(brd->print_driver); + if (rc < 0) + goto unregister_serial_drv; + dgap_boards_by_major[brd->serial_driver->major] = brd; + brd->dgap_serial_major = brd->serial_driver->major; -static ssize_t dgap_driver_maxboards_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", MAXBOARDS); -} -static DRIVER_ATTR(maxboards, S_IRUSR, dgap_driver_maxboards_show, NULL); + dgap_boards_by_major[brd->print_driver->major] = brd; + brd->dgap_transparent_print_major = brd->print_driver->major; + return 0; -static ssize_t dgap_driver_pollcounter_show(struct device_driver *ddp, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%ld\n", dgap_poll_counter); -} -static DRIVER_ATTR(pollcounter, S_IRUSR, dgap_driver_pollcounter_show, NULL); +unregister_serial_drv: + tty_unregister_driver(brd->serial_driver); +free_print_drv: + put_tty_driver(brd->print_driver); +free_serial_drv: + put_tty_driver(brd->serial_driver); -static ssize_t dgap_driver_pollrate_show(struct device_driver *ddp, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%dms\n", dgap_poll_tick); + return rc; } -static ssize_t dgap_driver_pollrate_store(struct device_driver *ddp, - const char *buf, size_t count) +static void dgap_tty_unregister(struct board_t *brd) { - if (sscanf(buf, "%d\n", &dgap_poll_tick) != 1) - return -EINVAL; - return count; + tty_unregister_driver(brd->print_driver); + tty_unregister_driver(brd->serial_driver); + put_tty_driver(brd->print_driver); + put_tty_driver(brd->serial_driver); } -static DRIVER_ATTR(pollrate, (S_IRUSR | S_IWUSR), dgap_driver_pollrate_show, - dgap_driver_pollrate_store); -static int dgap_create_driver_sysfiles(struct pci_driver *dgap_driver) -{ - int rc = 0; - struct device_driver *driverfs = &dgap_driver->driver; +static int dgap_alloc_flipbuf(struct board_t *brd) +{ + /* + * allocate flip buffer for board. + */ + brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); + if (!brd->flipbuf) + return -ENOMEM; - rc |= driver_create_file(driverfs, &driver_attr_version); - rc |= driver_create_file(driverfs, &driver_attr_boards); - rc |= driver_create_file(driverfs, &driver_attr_maxboards); - rc |= driver_create_file(driverfs, &driver_attr_pollrate); - rc |= driver_create_file(driverfs, &driver_attr_pollcounter); + brd->flipflagbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); + if (!brd->flipflagbuf) { + kfree(brd->flipbuf); + return -ENOMEM; + } - return rc; + return 0; } -static void dgap_remove_driver_sysfiles(struct pci_driver *dgap_driver) +static void dgap_free_flipbuf(struct board_t *brd) { - struct device_driver *driverfs = &dgap_driver->driver; - - driver_remove_file(driverfs, &driver_attr_version); - driver_remove_file(driverfs, &driver_attr_boards); - driver_remove_file(driverfs, &driver_attr_maxboards); - driver_remove_file(driverfs, &driver_attr_pollrate); - driver_remove_file(driverfs, &driver_attr_pollcounter); + kfree(brd->flipbuf); + kfree(brd->flipflagbuf); } static struct board_t *dgap_verify_board(struct device *p) @@ -5843,42 +5600,227 @@ static ssize_t dgap_ports_txcount_show(struct device *p, } static DEVICE_ATTR(ports_txcount, S_IRUSR, dgap_ports_txcount_show, NULL); -/* this function creates the sys files that will export each signal status - * to sysfs each value will be put in a separate filename - */ -static void dgap_create_ports_sysfiles(struct board_t *bd) +static ssize_t dgap_tty_state_show(struct device *d, + struct device_attribute *attr, + char *buf) { - dev_set_drvdata(&bd->pdev->dev, bd); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_state); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_baud); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_msignals); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_iflag); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_cflag); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_oflag); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_lflag); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_rxcount); - device_create_file(&(bd->pdev->dev), &dev_attr_ports_txcount); + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; + + return snprintf(buf, PAGE_SIZE, "%s", un->un_open_count ? + "Open" : "Closed"); +} +static DEVICE_ATTR(state, S_IRUSR, dgap_tty_state_show, NULL); + +static ssize_t dgap_tty_baud_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; + + return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_baud_info); +} +static DEVICE_ATTR(baud, S_IRUSR, dgap_tty_baud_show, NULL); + +static ssize_t dgap_tty_msignals_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; + + if (ch->ch_open_count) { + return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n", + (ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "", + (ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "", + (ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "", + (ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "", + (ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "", + (ch->ch_mistat & UART_MSR_RI) ? "RI" : ""); + } + return 0; +} +static DEVICE_ATTR(msignals, S_IRUSR, dgap_tty_msignals_show, NULL); + +static ssize_t dgap_tty_iflag_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; + + return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag); +} +static DEVICE_ATTR(iflag, S_IRUSR, dgap_tty_iflag_show, NULL); + +static ssize_t dgap_tty_cflag_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; + + return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag); +} +static DEVICE_ATTR(cflag, S_IRUSR, dgap_tty_cflag_show, NULL); + +static ssize_t dgap_tty_oflag_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; + + return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag); +} +static DEVICE_ATTR(oflag, S_IRUSR, dgap_tty_oflag_show, NULL); + +static ssize_t dgap_tty_lflag_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; + + return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag); } +static DEVICE_ATTR(lflag, S_IRUSR, dgap_tty_lflag_show, NULL); + +static ssize_t dgap_tty_digi_flag_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!d) + return 0; + un = dev_get_drvdata(d); + if (!un || un->magic != DGAP_UNIT_MAGIC) + return 0; + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return 0; + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return 0; + if (bd->state != BOARD_READY) + return 0; -/* removes all the sys files created for that port */ -static void dgap_remove_ports_sysfiles(struct board_t *bd) -{ - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_state); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_baud); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_msignals); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_iflag); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_cflag); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_oflag); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_lflag); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_rxcount); - device_remove_file(&(bd->pdev->dev), &dev_attr_ports_txcount); + return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags); } +static DEVICE_ATTR(digi_flag, S_IRUSR, dgap_tty_digi_flag_show, NULL); -static ssize_t dgap_tty_state_show(struct device *d, - struct device_attribute *attr, - char *buf) +static ssize_t dgap_tty_rxcount_show(struct device *d, + struct device_attribute *attr, + char *buf) { struct board_t *bd; struct channel_t *ch; @@ -5898,14 +5840,13 @@ static ssize_t dgap_tty_state_show(struct device *d, if (bd->state != BOARD_READY) return 0; - return snprintf(buf, PAGE_SIZE, "%s", un->un_open_count ? - "Open" : "Closed"); + return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount); } -static DEVICE_ATTR(state, S_IRUSR, dgap_tty_state_show, NULL); +static DEVICE_ATTR(rxcount, S_IRUSR, dgap_tty_rxcount_show, NULL); -static ssize_t dgap_tty_baud_show(struct device *d, - struct device_attribute *attr, - char *buf) +static ssize_t dgap_tty_txcount_show(struct device *d, + struct device_attribute *attr, + char *buf) { struct board_t *bd; struct channel_t *ch; @@ -5925,17 +5866,24 @@ static ssize_t dgap_tty_baud_show(struct device *d, if (bd->state != BOARD_READY) return 0; - return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_baud_info); + return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount); } -static DEVICE_ATTR(baud, S_IRUSR, dgap_tty_baud_show, NULL); +static DEVICE_ATTR(txcount, S_IRUSR, dgap_tty_txcount_show, NULL); -static ssize_t dgap_tty_msignals_show(struct device *d, - struct device_attribute *attr, - char *buf) +static ssize_t dgap_tty_name_show(struct device *d, + struct device_attribute *attr, + char *buf) { struct board_t *bd; struct channel_t *ch; struct un_t *un; + int cn; + int bn; + struct cnode *cptr; + int found = FALSE; + int ncount = 0; + int starto = 0; + int i; if (!d) return 0; @@ -5951,324 +5899,562 @@ static ssize_t dgap_tty_msignals_show(struct device *d, if (bd->state != BOARD_READY) return 0; - if (ch->ch_open_count) { - return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n", - (ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "", - (ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "", - (ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "", - (ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "", - (ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "", - (ch->ch_mistat & UART_MSR_RI) ? "RI" : ""); + bn = bd->boardnum; + cn = ch->ch_portnum; + + for (cptr = bd->bd_config; cptr; cptr = cptr->next) { + + if ((cptr->type == BNODE) && + ((cptr->u.board.type == APORT2_920P) || + (cptr->u.board.type == APORT4_920P) || + (cptr->u.board.type == APORT8_920P) || + (cptr->u.board.type == PAPORT4) || + (cptr->u.board.type == PAPORT8))) { + + found = TRUE; + if (cptr->u.board.v_start) + starto = cptr->u.board.start; + else + starto = 1; + } + + if (cptr->type == TNODE && found == TRUE) { + char *ptr1; + + if (strstr(cptr->u.ttyname, "tty")) { + ptr1 = cptr->u.ttyname; + ptr1 += 3; + } else + ptr1 = cptr->u.ttyname; + + for (i = 0; i < dgap_config_get_num_prts(bd); i++) { + if (cn != i) + continue; + + return snprintf(buf, PAGE_SIZE, "%s%s%02d\n", + (un->un_type == DGAP_PRINT) ? + "pr" : "tty", + ptr1, i + starto); + } + } + + if (cptr->type == CNODE) { + + for (i = 0; i < cptr->u.conc.nport; i++) { + if (cn != (i + ncount)) + continue; + + return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n", + (un->un_type == DGAP_PRINT) ? + "pr" : "tty", + cptr->u.conc.id, + i + (cptr->u.conc.v_start ? + cptr->u.conc.start : 1)); + } + + ncount += cptr->u.conc.nport; + } + + if (cptr->type == MNODE) { + + for (i = 0; i < cptr->u.module.nport; i++) { + if (cn != (i + ncount)) + continue; + + return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n", + (un->un_type == DGAP_PRINT) ? + "pr" : "tty", + cptr->u.module.id, + i + (cptr->u.module.v_start ? + cptr->u.module.start : 1)); + } + + ncount += cptr->u.module.nport; + } } - return 0; + + return snprintf(buf, PAGE_SIZE, "%s_dgap_%d_%d\n", + (un->un_type == DGAP_PRINT) ? "pr" : "tty", bn, cn); +} +static DEVICE_ATTR(custom_name, S_IRUSR, dgap_tty_name_show, NULL); + +static struct attribute *dgap_sysfs_tty_entries[] = { + &dev_attr_state.attr, + &dev_attr_baud.attr, + &dev_attr_msignals.attr, + &dev_attr_iflag.attr, + &dev_attr_cflag.attr, + &dev_attr_oflag.attr, + &dev_attr_lflag.attr, + &dev_attr_digi_flag.attr, + &dev_attr_rxcount.attr, + &dev_attr_txcount.attr, + &dev_attr_custom_name.attr, + NULL +}; + + +/* this function creates the sys files that will export each signal status + * to sysfs each value will be put in a separate filename + */ +static void dgap_create_ports_sysfiles(struct board_t *bd) +{ + dev_set_drvdata(&bd->pdev->dev, bd); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_state); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_baud); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_msignals); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_iflag); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_cflag); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_oflag); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_lflag); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_rxcount); + device_create_file(&(bd->pdev->dev), &dev_attr_ports_txcount); +} + +/* removes all the sys files created for that port */ +static void dgap_remove_ports_sysfiles(struct board_t *bd) +{ + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_state); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_baud); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_msignals); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_iflag); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_cflag); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_oflag); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_lflag); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_rxcount); + device_remove_file(&(bd->pdev->dev), &dev_attr_ports_txcount); +} + +/* + * Copies the BIOS code from the user to the board, + * and starts the BIOS running. + */ +static void dgap_do_bios_load(struct board_t *brd, const u8 *ubios, int len) +{ + u8 __iomem *addr; + uint offset; + unsigned int i; + + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return; + + addr = brd->re_map_membase; + + /* + * clear POST area + */ + for (i = 0; i < 16; i++) + writeb(0, addr + POSTAREA + i); + + /* + * Download bios + */ + offset = 0x1000; + memcpy_toio(addr + offset, ubios, len); + + writel(0x0bf00401, addr); + writel(0, (addr + 4)); + + /* Clear the reset, and change states. */ + writeb(FEPCLR, brd->re_map_port); +} + +/* + * Checks to see if the BIOS completed running on the card. + */ +static int dgap_test_bios(struct board_t *brd) +{ + u8 __iomem *addr; + u16 word; + u16 err1; + u16 err2; + + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return -EINVAL; + + addr = brd->re_map_membase; + word = readw(addr + POSTAREA); + + /* + * It can take 5-6 seconds for a board to + * pass the bios self test and post results. + * Give it 10 seconds. + */ + brd->wait_for_bios = 0; + while (brd->wait_for_bios < 1000) { + /* Check to see if BIOS thinks board is good. (GD). */ + if (word == *(u16 *) "GD") + return 0; + msleep_interruptible(10); + brd->wait_for_bios++; + word = readw(addr + POSTAREA); + } + + /* Gave up on board after too long of time taken */ + err1 = readw(addr + SEQUENCE); + err2 = readw(addr + ERROR); + dev_warn(&brd->pdev->dev, "%s failed diagnostics. Error #(%x,%x).\n", + brd->name, err1, err2); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOBIOS; + + return -EIO; } -static DEVICE_ATTR(msignals, S_IRUSR, dgap_tty_msignals_show, NULL); -static ssize_t dgap_tty_iflag_show(struct device *d, - struct device_attribute *attr, - char *buf) +/* + * Copies the FEP code from the user to the board, + * and starts the FEP running. + */ +static void dgap_do_fep_load(struct board_t *brd, const u8 *ufep, int len) { - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + u8 __iomem *addr; + uint offset; - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return; - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag); -} -static DEVICE_ATTR(iflag, S_IRUSR, dgap_tty_iflag_show, NULL); + addr = brd->re_map_membase; -static ssize_t dgap_tty_cflag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + /* + * Download FEP + */ + offset = 0x1000; + memcpy_toio(addr + offset, ufep, len); - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + /* + * If board is a concentrator product, we need to give + * it its config string describing how the concentrators look. + */ + if ((brd->type == PCX) || (brd->type == PEPC)) { + u8 string[100]; + u8 __iomem *config; + u8 *xconfig; + unsigned int i = 0; - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag); -} -static DEVICE_ATTR(cflag, S_IRUSR, dgap_tty_cflag_show, NULL); + xconfig = dgap_create_config_string(brd, string); -static ssize_t dgap_tty_oflag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + /* Write string to board memory */ + config = addr + CONFIG; + for (; i < CONFIGSIZE; i++, config++, xconfig++) { + writeb(*xconfig, config); + if ((*xconfig & 0xff) == 0xff) + break; + } + } - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + writel(0xbfc01004, (addr + 0xc34)); + writel(0x3, (addr + 0xc30)); - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag); } -static DEVICE_ATTR(oflag, S_IRUSR, dgap_tty_oflag_show, NULL); -static ssize_t dgap_tty_lflag_show(struct device *d, - struct device_attribute *attr, - char *buf) +/* + * Waits for the FEP to report thats its ready for us to use. + */ +static int dgap_test_fep(struct board_t *brd) { - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + u8 __iomem *addr; + u16 word; + u16 err1; + u16 err2; - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return -EINVAL; - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag); -} -static DEVICE_ATTR(lflag, S_IRUSR, dgap_tty_lflag_show, NULL); + addr = brd->re_map_membase; + word = readw(addr + FEPSTAT); -static ssize_t dgap_tty_digi_flag_show(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + /* + * It can take 2-3 seconds for the FEP to + * be up and running. Give it 5 secs. + */ + brd->wait_for_fep = 0; + while (brd->wait_for_fep < 500) { + /* Check to see if FEP is up and running now. */ + if (word == *(u16 *) "OS") { + /* + * Check to see if the board can support FEP5+ commands. + */ + word = readw(addr + FEP5_PLUS); + if (word == *(u16 *) "5A") + brd->bd_flags |= BD_FEP5PLUS; - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + return 0; + } + msleep_interruptible(10); + brd->wait_for_fep++; + word = readw(addr + FEPSTAT); + } - return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags); + /* Gave up on board after too long of time taken */ + err1 = readw(addr + SEQUENCE); + err2 = readw(addr + ERROR); + dev_warn(&brd->pdev->dev, + "FEPOS for %s not functioning. Error #(%x,%x).\n", + brd->name, err1, err2); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + + return -EIO; } -static DEVICE_ATTR(digi_flag, S_IRUSR, dgap_tty_digi_flag_show, NULL); -static ssize_t dgap_tty_rxcount_show(struct device *d, - struct device_attribute *attr, - char *buf) +/* + * Physically forces the FEP5 card to reset itself. + */ +static void dgap_do_reset_board(struct board_t *brd) { - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + u8 check; + u32 check1; + u32 check2; + unsigned int i; - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || + !brd->re_map_membase || !brd->re_map_port) + return; - return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount); + /* FEPRST does not vary among supported boards */ + writeb(FEPRST, brd->re_map_port); + + for (i = 0; i <= 1000; i++) { + check = readb(brd->re_map_port) & 0xe; + if (check == FEPRST) + break; + udelay(10); + + } + if (i > 1000) { + dev_warn(&brd->pdev->dev, + "dgap: Board not resetting... Failing board.\n"); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return; + } + + /* + * Make sure there really is memory out there. + */ + writel(0xa55a3cc3, (brd->re_map_membase + LOWMEM)); + writel(0x5aa5c33c, (brd->re_map_membase + HIGHMEM)); + check1 = readl(brd->re_map_membase + LOWMEM); + check2 = readl(brd->re_map_membase + HIGHMEM); + + if ((check1 != 0xa55a3cc3) || (check2 != 0x5aa5c33c)) { + dev_warn(&brd->pdev->dev, + "No memory at %p for board.\n", + brd->re_map_membase); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return; + } } -static DEVICE_ATTR(rxcount, S_IRUSR, dgap_tty_rxcount_show, NULL); -static ssize_t dgap_tty_txcount_show(struct device *d, - struct device_attribute *attr, - char *buf) +#ifdef DIGI_CONCENTRATORS_SUPPORTED +/* + * Sends a concentrator image into the FEP5 board. + */ +static void dgap_do_conc_load(struct board_t *brd, u8 *uaddr, int len) { - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; + char __iomem *vaddr; + u16 offset; + struct downld_t *to_dp; - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return; - return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount); + vaddr = brd->re_map_membase; + + offset = readw((u16 *) (vaddr + DOWNREQ)); + to_dp = (struct downld_t *) (vaddr + (int) offset); + memcpy_toio(to_dp, uaddr, len); + + /* Tell card we have data for it */ + writew(0, vaddr + (DOWNREQ)); + + brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; } -static DEVICE_ATTR(txcount, S_IRUSR, dgap_tty_txcount_show, NULL); +#endif -static ssize_t dgap_tty_name_show(struct device *d, - struct device_attribute *attr, - char *buf) +#define EXPANSION_ROM_SIZE (64 * 1024) +#define FEP5_ROM_MAGIC (0xFEFFFFFF) + +static void dgap_get_vpd(struct board_t *brd) { - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - int cn; - int bn; - struct cnode *cptr; - int found = FALSE; - int ncount = 0; - int starto = 0; - int i; + u32 magic; + u32 base_offset; + u16 rom_offset; + u16 vpd_offset; + u16 image_length; + u16 i; + u8 byte1; + u8 byte2; - if (!d) - return 0; - un = dev_get_drvdata(d); - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - bd = ch->ch_bd; - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return 0; - if (bd->state != BOARD_READY) - return 0; + /* + * Poke the magic number at the PCI Rom Address location. + * If VPD is supported, the value read from that address + * will be non-zero. + */ + magic = FEP5_ROM_MAGIC; + pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); + pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic); - bn = bd->boardnum; - cn = ch->ch_portnum; + /* VPD not supported, bail */ + if (!magic) + return; - for (cptr = bd->bd_config; cptr; cptr = cptr->next) { + /* + * To get to the OTPROM memory, we have to send the boards base + * address or'ed with 1 into the PCI Rom Address location. + */ + magic = brd->membase | 0x01; + pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); + pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic); - if ((cptr->type == BNODE) && - ((cptr->u.board.type == APORT2_920P) || - (cptr->u.board.type == APORT4_920P) || - (cptr->u.board.type == APORT8_920P) || - (cptr->u.board.type == PAPORT4) || - (cptr->u.board.type == PAPORT8))) { + byte1 = readb(brd->re_map_membase); + byte2 = readb(brd->re_map_membase + 1); - found = TRUE; - if (cptr->u.board.v_start) - starto = cptr->u.board.start; - else - starto = 1; - } + /* + * If the board correctly swapped to the OTPROM memory, + * the first 2 bytes (header) should be 0x55, 0xAA + */ + if (byte1 == 0x55 && byte2 == 0xAA) { - if (cptr->type == TNODE && found == TRUE) { - char *ptr1; + base_offset = 0; - if (strstr(cptr->u.ttyname, "tty")) { - ptr1 = cptr->u.ttyname; - ptr1 += 3; - } else - ptr1 = cptr->u.ttyname; + /* + * We have to run through all the OTPROM memory looking + * for the VPD offset. + */ + while (base_offset <= EXPANSION_ROM_SIZE) { - for (i = 0; i < dgap_config_get_num_prts(bd); i++) { - if (cn != i) - continue; + /* + * Lots of magic numbers here. + * + * The VPD offset is located inside the ROM Data + * Structure. + * + * We also have to remember the length of each + * ROM Data Structure, so we can "hop" to the next + * entry if the VPD isn't in the current + * ROM Data Structure. + */ + rom_offset = readw(brd->re_map_membase + + base_offset + 0x18); + image_length = readw(brd->re_map_membase + + rom_offset + 0x10) * 512; + vpd_offset = readw(brd->re_map_membase + + rom_offset + 0x08); - return snprintf(buf, PAGE_SIZE, "%s%s%02d\n", - (un->un_type == DGAP_PRINT) ? - "pr" : "tty", - ptr1, i + starto); + /* Found the VPD entry */ + if (vpd_offset) + break; + + /* We didn't find a VPD entry, go to next ROM entry. */ + base_offset += image_length; + + byte1 = readb(brd->re_map_membase + base_offset); + byte2 = readb(brd->re_map_membase + base_offset + 1); + + /* + * If the new ROM offset doesn't have 0x55, 0xAA + * as its header, we have run out of ROM. + */ + if (byte1 != 0x55 || byte2 != 0xAA) + break; + } + + /* + * If we have a VPD offset, then mark the board + * as having a valid VPD, and copy VPDSIZE (512) bytes of + * that VPD to the buffer we have in our board structure. + */ + if (vpd_offset) { + brd->bd_flags |= BD_HAS_VPD; + for (i = 0; i < VPDSIZE; i++) { + brd->vpd[i] = readb(brd->re_map_membase + + vpd_offset + i); } } + } - if (cptr->type == CNODE) { + /* + * We MUST poke the magic number at the PCI Rom Address location again. + * This makes the card report the regular board memory back to us, + * rather than the OTPROM memory. + */ + magic = FEP5_ROM_MAGIC; + pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic); +} - for (i = 0; i < cptr->u.conc.nport; i++) { - if (cn != (i + ncount)) - continue; - return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n", - (un->un_type == DGAP_PRINT) ? - "pr" : "tty", - cptr->u.conc.id, - i + (cptr->u.conc.v_start ? - cptr->u.conc.start : 1)); - } +static ssize_t dgap_driver_version_show(struct device_driver *ddp, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", DG_PART); +} +static DRIVER_ATTR(version, S_IRUSR, dgap_driver_version_show, NULL); - ncount += cptr->u.conc.nport; - } - if (cptr->type == MNODE) { +static ssize_t dgap_driver_boards_show(struct device_driver *ddp, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", dgap_numboards); +} +static DRIVER_ATTR(boards, S_IRUSR, dgap_driver_boards_show, NULL); - for (i = 0; i < cptr->u.module.nport; i++) { - if (cn != (i + ncount)) - continue; - return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n", - (un->un_type == DGAP_PRINT) ? - "pr" : "tty", - cptr->u.module.id, - i + (cptr->u.module.v_start ? - cptr->u.module.start : 1)); - } +static ssize_t dgap_driver_maxboards_show(struct device_driver *ddp, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", MAXBOARDS); +} +static DRIVER_ATTR(maxboards, S_IRUSR, dgap_driver_maxboards_show, NULL); + + +static ssize_t dgap_driver_pollcounter_show(struct device_driver *ddp, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%ld\n", dgap_poll_counter); +} +static DRIVER_ATTR(pollcounter, S_IRUSR, dgap_driver_pollcounter_show, NULL); + +static ssize_t dgap_driver_pollrate_show(struct device_driver *ddp, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%dms\n", dgap_poll_tick); +} + +static ssize_t dgap_driver_pollrate_store(struct device_driver *ddp, + const char *buf, size_t count) +{ + if (sscanf(buf, "%d\n", &dgap_poll_tick) != 1) + return -EINVAL; + return count; +} +static DRIVER_ATTR(pollrate, (S_IRUSR | S_IWUSR), dgap_driver_pollrate_show, + dgap_driver_pollrate_store); - ncount += cptr->u.module.nport; - } - } +static int dgap_create_driver_sysfiles(struct pci_driver *dgap_driver) +{ + int rc = 0; + struct device_driver *driverfs = &dgap_driver->driver; - return snprintf(buf, PAGE_SIZE, "%s_dgap_%d_%d\n", - (un->un_type == DGAP_PRINT) ? "pr" : "tty", bn, cn); + rc |= driver_create_file(driverfs, &driver_attr_version); + rc |= driver_create_file(driverfs, &driver_attr_boards); + rc |= driver_create_file(driverfs, &driver_attr_maxboards); + rc |= driver_create_file(driverfs, &driver_attr_pollrate); + rc |= driver_create_file(driverfs, &driver_attr_pollcounter); + + return rc; } -static DEVICE_ATTR(custom_name, S_IRUSR, dgap_tty_name_show, NULL); -static struct attribute *dgap_sysfs_tty_entries[] = { - &dev_attr_state.attr, - &dev_attr_baud.attr, - &dev_attr_msignals.attr, - &dev_attr_iflag.attr, - &dev_attr_cflag.attr, - &dev_attr_oflag.attr, - &dev_attr_lflag.attr, - &dev_attr_digi_flag.attr, - &dev_attr_rxcount.attr, - &dev_attr_txcount.attr, - &dev_attr_custom_name.attr, - NULL -}; +static void dgap_remove_driver_sysfiles(struct pci_driver *dgap_driver) +{ + struct device_driver *driverfs = &dgap_driver->driver; + + driver_remove_file(driverfs, &driver_attr_version); + driver_remove_file(driverfs, &driver_attr_boards); + driver_remove_file(driverfs, &driver_attr_maxboards); + driver_remove_file(driverfs, &driver_attr_pollrate); + driver_remove_file(driverfs, &driver_attr_pollcounter); +} static struct attribute_group dgap_tty_attribute_group = { .name = NULL, @@ -6292,1064 +6478,753 @@ static void dgap_remove_tty_sysfs(struct device *c) sysfs_remove_group(&c->kobj, &dgap_tty_attribute_group); } -static void dgap_cleanup_nodes(void) -{ - struct cnode *p; - - p = &dgap_head; - - while (p) { - struct cnode *tmp = p->next; - - if (p->type == NULLNODE) { - p = tmp; - continue; - } - - switch (p->type) { - case BNODE: - kfree(p->u.board.portstr); - kfree(p->u.board.addrstr); - kfree(p->u.board.pcibusstr); - kfree(p->u.board.pcislotstr); - kfree(p->u.board.method); - break; - case CNODE: - kfree(p->u.conc.id); - kfree(p->u.conc.connect); - break; - case MNODE: - kfree(p->u.module.id); - break; - case TNODE: - kfree(p->u.ttyname); - break; - case CUNODE: - kfree(p->u.cuname); - break; - case LNODE: - kfree(p->u.line.cable); - break; - case PNODE: - kfree(p->u.printname); - break; - } - - kfree(p->u.board.status); - kfree(p); - p = tmp; - } -} /* - * Parse a configuration file read into memory as a string. + * Create pr and tty device entries */ -static int dgap_parsefile(char **in) +static int dgap_tty_register_ports(struct board_t *brd) { - struct cnode *p, *brd, *line, *conc; - int rc; - char *s; - int linecnt = 0; - - p = &dgap_head; - brd = line = conc = NULL; + struct channel_t *ch; + int i; + int ret; - /* perhaps we are adding to an existing list? */ - while (p->next) - p = p->next; + brd->serial_ports = kcalloc(brd->nasync, sizeof(*brd->serial_ports), + GFP_KERNEL); + if (!brd->serial_ports) + return -ENOMEM; - /* file must start with a BEGIN */ - while ((rc = dgap_gettok(in)) != BEGIN) { - if (rc == 0) { - pr_err("unexpected EOF"); - return -1; - } + brd->printer_ports = kcalloc(brd->nasync, sizeof(*brd->printer_ports), + GFP_KERNEL); + if (!brd->printer_ports) { + ret = -ENOMEM; + goto free_serial_ports; } - for (; ;) { - int board_type = 0; - int conc_type = 0; - int module_type = 0; - - rc = dgap_gettok(in); - if (rc == 0) { - pr_err("unexpected EOF"); - return -1; - } - - switch (rc) { - case BEGIN: /* should only be 1 begin */ - pr_err("unexpected config_begin\n"); - return -1; - - case END: - return 0; - - case BOARD: /* board info */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; - - p = p->next; - - p->type = BNODE; - p->u.board.status = kstrdup("No", GFP_KERNEL); - line = conc = NULL; - brd = p; - linecnt = -1; + for (i = 0; i < brd->nasync; i++) { + tty_port_init(&brd->serial_ports[i]); + tty_port_init(&brd->printer_ports[i]); + } - board_type = dgap_gettok(in); - if (board_type == 0) { - pr_err("board !!type not specified"); - return -1; - } + ch = brd->channels[0]; + for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { - p->u.board.type = board_type; + struct device *classp; - break; + classp = tty_port_register_device(&brd->serial_ports[i], + brd->serial_driver, + i, NULL); - case IO: /* i/o port */ - if (p->type != BNODE) { - pr_err("IO port only vaild for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.board.portstr = kstrdup(s, GFP_KERNEL); - if (kstrtol(s, 0, &p->u.board.port)) { - pr_err("bad number for IO port"); - return -1; - } - p->u.board.v_port = 1; - break; + if (IS_ERR(classp)) { + ret = PTR_ERR(classp); + goto unregister_ttys; + } - case MEM: /* memory address */ - if (p->type != BNODE) { - pr_err("memory address only vaild for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.board.addrstr = kstrdup(s, GFP_KERNEL); - if (kstrtoul(s, 0, &p->u.board.addr)) { - pr_err("bad number for memory address"); - return -1; - } - p->u.board.v_addr = 1; - break; + dgap_create_tty_sysfs(&ch->ch_tun, classp); + ch->ch_tun.un_sysfs = classp; - case PCIINFO: /* pci information */ - if (p->type != BNODE) { - pr_err("memory address only vaild for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.board.pcibusstr = kstrdup(s, GFP_KERNEL); - if (kstrtoul(s, 0, &p->u.board.pcibus)) { - pr_err("bad number for pci bus"); - return -1; - } - p->u.board.v_pcibus = 1; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.board.pcislotstr = kstrdup(s, GFP_KERNEL); - if (kstrtoul(s, 0, &p->u.board.pcislot)) { - pr_err("bad number for pci slot"); - return -1; - } - p->u.board.v_pcislot = 1; - break; + classp = tty_port_register_device(&brd->printer_ports[i], + brd->print_driver, + i, NULL); - case METHOD: - if (p->type != BNODE) { - pr_err("install method only vaild for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.board.method = kstrdup(s, GFP_KERNEL); - p->u.board.v_method = 1; - break; + if (IS_ERR(classp)) { + ret = PTR_ERR(classp); + goto unregister_ttys; + } - case STATUS: - if (p->type != BNODE) { - pr_err("config status only vaild for boards"); - return -1; - } - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.board.status = kstrdup(s, GFP_KERNEL); - break; + dgap_create_tty_sysfs(&ch->ch_pun, classp); + ch->ch_pun.un_sysfs = classp; + } + dgap_create_ports_sysfiles(brd); - case NPORTS: /* number of ports */ - if (p->type == BNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.board.nport)) { - pr_err("bad number for number of ports"); - return -1; - } - p->u.board.v_nport = 1; - } else if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.conc.nport)) { - pr_err("bad number for number of ports"); - return -1; - } - p->u.conc.v_nport = 1; - } else if (p->type == MNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.module.nport)) { - pr_err("bad number for number of ports"); - return -1; - } - p->u.module.v_nport = 1; - } else { - pr_err("nports only valid for concentrators or modules"); - return -1; - } - break; + return 0; - case ID: /* letter ID used in tty name */ - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } +unregister_ttys: + while (i >= 0) { + ch = brd->channels[i]; + if (ch->ch_tun.un_sysfs) { + dgap_remove_tty_sysfs(ch->ch_tun.un_sysfs); + tty_unregister_device(brd->serial_driver, i); + } - p->u.board.status = kstrdup(s, GFP_KERNEL); + if (ch->ch_pun.un_sysfs) { + dgap_remove_tty_sysfs(ch->ch_pun.un_sysfs); + tty_unregister_device(brd->print_driver, i); + } + i--; + } - if (p->type == CNODE) { - p->u.conc.id = kstrdup(s, GFP_KERNEL); - p->u.conc.v_id = 1; - } else if (p->type == MNODE) { - p->u.module.id = kstrdup(s, GFP_KERNEL); - p->u.module.v_id = 1; - } else { - pr_err("id only valid for concentrators or modules"); - return -1; - } - break; + for (i = 0; i < brd->nasync; i++) { + tty_port_destroy(&brd->serial_ports[i]); + tty_port_destroy(&brd->printer_ports[i]); + } - case STARTO: /* start offset of ID */ - if (p->type == BNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.board.start)) { - pr_err("bad number for start of tty count"); - return -1; - } - p->u.board.v_start = 1; - } else if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.conc.start)) { - pr_err("bad number for start of tty count"); - return -1; - } - p->u.conc.v_start = 1; - } else if (p->type == MNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.module.start)) { - pr_err("bad number for start of tty count"); - return -1; - } - p->u.module.v_start = 1; - } else { - pr_err("start only valid for concentrators or modules"); - return -1; - } - break; + kfree(brd->printer_ports); + brd->printer_ports = NULL; - case TTYN: /* tty name prefix */ - if (dgap_checknode(p)) - return -1; +free_serial_ports: + kfree(brd->serial_ports); + brd->serial_ports = NULL; - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + return ret; +} - p = p->next; - p->type = TNODE; +/* + * dgap_cleanup_tty() + * + * Uninitialize the TTY portion of this driver. Free all memory and + * resources. + */ +static void dgap_cleanup_tty(struct board_t *brd) +{ + struct device *dev; + unsigned int i; - s = dgap_getword(in); - if (!s) { - pr_err("unexpeced end of file"); - return -1; - } - p->u.ttyname = kstrdup(s, GFP_KERNEL); - if (!p->u.ttyname) - return -1; + dgap_boards_by_major[brd->serial_driver->major] = NULL; + brd->dgap_serial_major = 0; + for (i = 0; i < brd->nasync; i++) { + tty_port_destroy(&brd->serial_ports[i]); + dev = brd->channels[i]->ch_tun.un_sysfs; + dgap_remove_tty_sysfs(dev); + tty_unregister_device(brd->serial_driver, i); + } + tty_unregister_driver(brd->serial_driver); + put_tty_driver(brd->serial_driver); + kfree(brd->serial_ports); - break; + dgap_boards_by_major[brd->print_driver->major] = NULL; + brd->dgap_transparent_print_major = 0; + for (i = 0; i < brd->nasync; i++) { + tty_port_destroy(&brd->printer_ports[i]); + dev = brd->channels[i]->ch_pun.un_sysfs; + dgap_remove_tty_sysfs(dev); + tty_unregister_device(brd->print_driver, i); + } + tty_unregister_driver(brd->print_driver); + put_tty_driver(brd->print_driver); + kfree(brd->printer_ports); +} - case CU: /* cu name prefix */ - if (dgap_checknode(p)) - return -1; +static int dgap_request_irq(struct board_t *brd) +{ + int rc; - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return -ENODEV; - p = p->next; - p->type = CUNODE; + /* + * Set up our interrupt handler if we are set to do interrupts. + */ + if (dgap_config_get_useintr(brd) && brd->irq) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpeced end of file"); - return -1; - } - p->u.cuname = kstrdup(s, GFP_KERNEL); - if (!p->u.cuname) - return -1; + rc = request_irq(brd->irq, dgap_intr, IRQF_SHARED, "DGAP", brd); - break; + if (!rc) + brd->intr_used = 1; + } + return 0; +} - case LINE: /* line information */ - if (dgap_checknode(p)) - return -1; - if (!brd) { - pr_err("must specify board before line info"); - return -1; - } - switch (brd->u.board.type) { - case PPCM: - pr_err("line not vaild for PC/em"); - return -1; - } +static void dgap_free_irq(struct board_t *brd) +{ + if (brd->intr_used && brd->irq) + free_irq(brd->irq, brd); +} + +static int dgap_firmware_load(struct pci_dev *pdev, int card_type, + struct board_t *brd) +{ + const struct firmware *fw; + char *tmp_ptr; + int ret; + char *dgap_config_buf; + + dgap_get_vpd(brd); + dgap_do_reset_board(brd); + + if (fw_info[card_type].conf_name) { + ret = request_firmware(&fw, fw_info[card_type].conf_name, + &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "config file %s not found\n", + fw_info[card_type].conf_name); + return ret; + } - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + dgap_config_buf = kzalloc(fw->size + 1, GFP_KERNEL); + if (!dgap_config_buf) { + release_firmware(fw); + return -ENOMEM; + } - p = p->next; - p->type = LNODE; - conc = NULL; - line = p; - linecnt++; - break; + memcpy(dgap_config_buf, fw->data, fw->size); + release_firmware(fw); - case CONC: /* concentrator information */ - if (dgap_checknode(p)) - return -1; - if (!line) { - pr_err("must specify line info before concentrator"); - return -1; - } + /* + * preserve dgap_config_buf + * as dgap_parsefile would + * otherwise alter it. + */ + tmp_ptr = dgap_config_buf; - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + if (dgap_parsefile(&tmp_ptr) != 0) { + kfree(dgap_config_buf); + return -EINVAL; + } + kfree(dgap_config_buf); + } - p = p->next; - p->type = CNODE; - conc = p; + /* + * Match this board to a config the user created for us. + */ + brd->bd_config = + dgap_find_config(brd->type, brd->pci_bus, brd->pci_slot); - if (linecnt) - brd->u.board.conc2++; - else - brd->u.board.conc1++; + /* + * Because the 4 port Xr products share the same PCI ID + * as the 8 port Xr products, if we receive a NULL config + * back, and this is a PAPORT8 board, retry with a + * PAPORT4 attempt as well. + */ + if (brd->type == PAPORT8 && !brd->bd_config) + brd->bd_config = + dgap_find_config(PAPORT4, brd->pci_bus, brd->pci_slot); - conc_type = dgap_gettok(in); - if (conc_type == 0 || conc_type != CX || - conc_type != EPC) { - pr_err("failed to set a type of concentratros"); - return -1; - } + if (!brd->bd_config) { + dev_err(&pdev->dev, "No valid configuration found\n"); + return -EINVAL; + } - p->u.conc.type = conc_type; + if (fw_info[card_type].bios_name) { + ret = request_firmware(&fw, fw_info[card_type].bios_name, + &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "bios file %s not found\n", + fw_info[card_type].bios_name); + return ret; + } + dgap_do_bios_load(brd, fw->data, fw->size); + release_firmware(fw); - break; + /* Wait for BIOS to test board... */ + ret = dgap_test_bios(brd); + if (ret) + return ret; + } - case MOD: /* EBI module */ - if (dgap_checknode(p)) - return -1; - if (!brd) { - pr_err("must specify board info before EBI modules"); - return -1; - } - switch (brd->u.board.type) { - case PPCM: - linecnt = 0; - break; - default: - if (!conc) { - pr_err("must specify concentrator info before EBI module"); - return -1; - } - } + if (fw_info[card_type].fep_name) { + ret = request_firmware(&fw, fw_info[card_type].fep_name, + &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "dgap: fep file %s not found\n", + fw_info[card_type].fep_name); + return ret; + } + dgap_do_fep_load(brd, fw->data, fw->size); + release_firmware(fw); - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + /* Wait for FEP to load on board... */ + ret = dgap_test_fep(brd); + if (ret) + return ret; + } - p = p->next; - p->type = MNODE; +#ifdef DIGI_CONCENTRATORS_SUPPORTED + /* + * If this is a CX or EPCX, we need to see if the firmware + * is requesting a concentrator image from us. + */ + if ((bd->type == PCX) || (bd->type == PEPC)) { + chk_addr = (u16 *) (vaddr + DOWNREQ); + /* Nonzero if FEP is requesting concentrator image. */ + check = readw(chk_addr); + vaddr = brd->re_map_membase; + } - if (linecnt) - brd->u.board.module2++; - else - brd->u.board.module1++; + if (fw_info[card_type].con_name && check && vaddr) { + ret = request_firmware(&fw, fw_info[card_type].con_name, + &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "conc file %s not found\n", + fw_info[card_type].con_name); + return ret; + } + /* Put concentrator firmware loading code here */ + offset = readw((u16 *) (vaddr + DOWNREQ)); + memcpy_toio(offset, fw->data, fw->size); - module_type = dgap_gettok(in); - if (module_type == 0 || module_type != PORTS || - module_type != MODEM) { - pr_err("failed to set a type of module"); - return -1; - } + dgap_do_conc_load(brd, (char *)fw->data, fw->size) + release_firmware(fw); + } +#endif - p->u.module.type = module_type; + return 0; +} - break; +/* + * dgap_tty_init() + * + * Init the tty subsystem. Called once per board after board has been + * downloaded and init'ed. + */ +static int dgap_tty_init(struct board_t *brd) +{ + int i; + int tlw; + uint true_count; + u8 __iomem *vaddr; + u8 modem; + struct channel_t *ch; + struct bs_t __iomem *bs; + struct cm_t __iomem *cm; + int ret; - case CABLE: - if (p->type == LNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.line.cable = kstrdup(s, GFP_KERNEL); - p->u.line.v_cable = 1; - } - break; + /* + * Initialize board structure elements. + */ - case SPEED: /* sync line speed indication */ - if (p->type == LNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.line.speed)) { - pr_err("bad number for line speed"); - return -1; - } - p->u.line.v_speed = 1; - } else if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.conc.speed)) { - pr_err("bad number for line speed"); - return -1; - } - p->u.conc.v_speed = 1; - } else { - pr_err("speed valid only for lines or concentrators."); - return -1; - } - break; + vaddr = brd->re_map_membase; + true_count = readw((vaddr + NCHAN)); - case CONNECT: - if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - p->u.conc.connect = kstrdup(s, GFP_KERNEL); - p->u.conc.v_connect = 1; - } - break; - case PRINT: /* transparent print name prefix */ - if (dgap_checknode(p)) - return -1; + brd->nasync = dgap_config_get_num_prts(brd); - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + if (!brd->nasync) + brd->nasync = brd->maxports; - p = p->next; - p->type = PNODE; + if (brd->nasync > brd->maxports) + brd->nasync = brd->maxports; + + if (true_count != brd->nasync) { + dev_warn(&brd->pdev->dev, + "%s configured for %d ports, has %d ports.\n", + brd->name, brd->nasync, true_count); + + if ((brd->type == PPCM) && + (true_count == 64 || true_count == 0)) { + dev_warn(&brd->pdev->dev, + "Please make SURE the EBI cable running from the card\n"); + dev_warn(&brd->pdev->dev, + "to each EM module is plugged into EBI IN!\n"); + } - s = dgap_getword(in); - if (!s) { - pr_err("unexpeced end of file"); - return -1; - } - p->u.printname = kstrdup(s, GFP_KERNEL); - if (!p->u.printname) - return -1; + brd->nasync = true_count; - break; + /* If no ports, don't bother going any further */ + if (!brd->nasync) { + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return -EIO; + } + } - case CMAJOR: /* major number */ - if (dgap_checknode(p)) - return -1; + /* + * Allocate channel memory that might not have been allocated + * when the driver was first loaded. + */ + for (i = 0; i < brd->nasync; i++) { + brd->channels[i] = + kzalloc(sizeof(struct channel_t), GFP_KERNEL); + if (!brd->channels[i]) { + ret = -ENOMEM; + goto free_chan; + } + } - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + ch = brd->channels[0]; + vaddr = brd->re_map_membase; - p = p->next; - p->type = JNODE; + bs = (struct bs_t __iomem *) ((ulong) vaddr + CHANBUF); + cm = (struct cm_t __iomem *) ((ulong) vaddr + CMDBUF); - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.majornumber)) { - pr_err("bad number for major number"); - return -1; - } - break; + brd->bd_bs = bs; - case ALTPIN: /* altpin setting */ - if (dgap_checknode(p)) - return -1; + /* Set up channel variables */ + for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) { - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + spin_lock_init(&ch->ch_lock); - p = p->next; - p->type = ANODE; + /* Store all our magic numbers */ + ch->magic = DGAP_CHANNEL_MAGIC; + ch->ch_tun.magic = DGAP_UNIT_MAGIC; + ch->ch_tun.un_type = DGAP_SERIAL; + ch->ch_tun.un_ch = ch; + ch->ch_tun.un_dev = i; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.altpin)) { - pr_err("bad number for altpin"); - return -1; - } - break; + ch->ch_pun.magic = DGAP_UNIT_MAGIC; + ch->ch_pun.un_type = DGAP_PRINT; + ch->ch_pun.un_ch = ch; + ch->ch_pun.un_dev = i; - case USEINTR: /* enable interrupt setting */ - if (dgap_checknode(p)) - return -1; + ch->ch_vaddr = vaddr; + ch->ch_bs = bs; + ch->ch_cm = cm; + ch->ch_bd = brd; + ch->ch_portnum = i; + ch->ch_digi = dgap_digi_init; - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + /* + * Set up digi dsr and dcd bits based on altpin flag. + */ + if (dgap_config_get_altpin(brd)) { + ch->ch_dsr = DM_CD; + ch->ch_cd = DM_DSR; + ch->ch_digi.digi_flags |= DIGI_ALTPIN; + } else { + ch->ch_cd = DM_CD; + ch->ch_dsr = DM_DSR; + } - p = p->next; - p->type = INTRNODE; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.useintr)) { - pr_err("bad number for useintr"); - return -1; - } - break; + ch->ch_taddr = vaddr + (ioread16(&(ch->ch_bs->tx_seg)) << 4); + ch->ch_raddr = vaddr + (ioread16(&(ch->ch_bs->rx_seg)) << 4); + ch->ch_tx_win = 0; + ch->ch_rx_win = 0; + ch->ch_tsize = readw(&(ch->ch_bs->tx_max)) + 1; + ch->ch_rsize = readw(&(ch->ch_bs->rx_max)) + 1; + ch->ch_tstart = 0; + ch->ch_rstart = 0; - case TTSIZ: /* size of tty structure */ - if (dgap_checknode(p)) - return -1; + /* + * Set queue water marks, interrupt mask, + * and general tty parameters. + */ + tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : + ch->ch_tsize / 2; + ch->ch_tlw = tlw; - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + dgap_cmdw(ch, STLOW, tlw, 0); - p = p->next; - p->type = TSNODE; + dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0); - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.ttysize)) { - pr_err("bad number for ttysize"); - return -1; - } - break; + dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0); - case CHSIZ: /* channel structure size */ - if (dgap_checknode(p)) - return -1; + ch->ch_mistat = readb(&(ch->ch_bs->m_stat)); - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + init_waitqueue_head(&ch->ch_flags_wait); + init_waitqueue_head(&ch->ch_tun.un_flags_wait); + init_waitqueue_head(&ch->ch_pun.un_flags_wait); - p = p->next; - p->type = CSNODE; + /* Turn on all modem interrupts for now */ + modem = (DM_CD | DM_DSR | DM_CTS | DM_RI); + writeb(modem, &(ch->ch_bs->m_int)); - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.chsize)) { - pr_err("bad number for chsize"); - return -1; - } - break; + /* + * Set edelay to 0 if interrupts are turned on, + * otherwise set edelay to the usual 100. + */ + if (brd->intr_used) + writew(0, &(ch->ch_bs->edelay)); + else + writew(100, &(ch->ch_bs->edelay)); - case BSSIZ: /* board structure size */ - if (dgap_checknode(p)) - return -1; + writeb(1, &(ch->ch_bs->idata)); + } - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + return 0; - p = p->next; - p->type = BSNODE; +free_chan: + while (--i >= 0) { + kfree(brd->channels[i]); + brd->channels[i] = NULL; + } + return ret; +} - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.bssize)) { - pr_err("bad number for bssize"); - return -1; - } - break; +/* + * dgap_tty_free() + * + * Free the channles which are allocated in dgap_tty_init(). + */ +static void dgap_tty_free(struct board_t *brd) +{ + int i; - case UNTSIZ: /* sched structure size */ - if (dgap_checknode(p)) - return -1; + for (i = 0; i < brd->nasync; i++) + kfree(brd->channels[i]); +} - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; +static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int rc; + struct board_t *brd; - p = p->next; - p->type = USNODE; + if (dgap_numboards >= MAXBOARDS) + return -EPERM; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.unsize)) { - pr_err("bad number for schedsize"); - return -1; - } - break; + rc = pci_enable_device(pdev); + if (rc) + return -EIO; - case F2SIZ: /* f2200 structure size */ - if (dgap_checknode(p)) - return -1; + brd = dgap_found_board(pdev, ent->driver_data, dgap_numboards); + if (IS_ERR(brd)) + return PTR_ERR(brd); - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + rc = dgap_firmware_load(pdev, ent->driver_data, brd); + if (rc) + goto cleanup_brd; - p = p->next; - p->type = FSNODE; + rc = dgap_alloc_flipbuf(brd); + if (rc) + goto cleanup_brd; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.f2size)) { - pr_err("bad number for f2200size"); - return -1; - } - break; + rc = dgap_tty_register(brd); + if (rc) + goto free_flipbuf; - case VPSIZ: /* vpix structure size */ - if (dgap_checknode(p)) - return -1; + rc = dgap_request_irq(brd); + if (rc) + goto unregister_tty; - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -1; + /* + * Do tty device initialization. + */ + rc = dgap_tty_init(brd); + if (rc < 0) + goto free_irq; - p = p->next; - p->type = VSNODE; + rc = dgap_tty_register_ports(brd); + if (rc) + goto tty_free; - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - if (kstrtol(s, 0, &p->u.vpixsize)) { - pr_err("bad number for vpixsize"); - return -1; - } - break; - } - } -} + brd->state = BOARD_READY; + brd->dpastatus = BD_RUNNING; -/* - * dgap_sindex: much like index(), but it looks for a match of any character in - * the group, and returns that position. If the first character is a ^, then - * this will match the first occurrence not in that group. - */ -static char *dgap_sindex(char *string, char *group) -{ - char *ptr; + dgap_board[dgap_numboards++] = brd; - if (!string || !group) - return NULL; + return 0; - if (*group == '^') { - group++; - for (; *string; string++) { - for (ptr = group; *ptr; ptr++) { - if (*ptr == *string) - break; - } - if (*ptr == '\0') - return string; - } - } else { - for (; *string; string++) { - for (ptr = group; *ptr; ptr++) { - if (*ptr == *string) - return string; - } - } - } +tty_free: + dgap_tty_free(brd); +free_irq: + dgap_free_irq(brd); +unregister_tty: + dgap_tty_unregister(brd); +free_flipbuf: + dgap_free_flipbuf(brd); +cleanup_brd: + dgap_cleanup_nodes(); + dgap_unmap(brd); + kfree(brd); - return NULL; + return rc; +} + +static void dgap_remove_one(struct pci_dev *dev) +{ + /* Do Nothing */ } +static struct pci_driver dgap_driver = { + .name = "dgap", + .probe = dgap_init_one, + .id_table = dgap_pci_tbl, + .remove = dgap_remove_one, +}; + /* - * Get a token from the input file; return 0 if end of file is reached + * dgap_init_globals() + * + * This is where we initialize the globals from the static insmod + * configuration variables. These are declared near the head of + * this file. */ -static int dgap_gettok(char **in) +static void dgap_init_globals(void) { - char *w; - struct toklist *t; + unsigned int i; - if (strstr(dgap_cword, "board")) { - w = dgap_getword(in); - snprintf(dgap_cword, MAXCWORD, "%s", w); - for (t = dgap_brdtype; t->token != 0; t++) { - if (!strcmp(w, t->string)) - return t->token; - } - } else { - while ((w = dgap_getword(in))) { - snprintf(dgap_cword, MAXCWORD, "%s", w); - for (t = dgap_tlist; t->token != 0; t++) { - if (!strcmp(w, t->string)) - return t->token; - } - } - } + for (i = 0; i < MAXBOARDS; i++) + dgap_board[i] = NULL; - return 0; + init_timer(&dgap_poll_timer); } /* - * get a word from the input stream, also keep track of current line number. - * words are separated by whitespace. + * Start of driver. */ -static char *dgap_getword(char **in) +static int dgap_start(void) { - char *ret_ptr = *in; + int rc; + unsigned long flags; + struct device *device; - char *ptr = dgap_sindex(*in, " \t\n"); + /* + * make sure that the globals are + * init'd before we do anything else + */ + dgap_init_globals(); - /* If no word found, return null */ - if (!ptr) - return NULL; + dgap_numboards = 0; - /* Mark new location for our buffer */ - *ptr = '\0'; - *in = ptr + 1; + pr_info("For the tools package please visit http://www.digi.com\n"); - /* Eat any extra spaces/tabs/newlines that might be present */ - while (*in && **in && ((**in == ' ') || - (**in == '\t') || - (**in == '\n'))) { - **in = '\0'; - *in = *in + 1; + /* + * Register our base character device into the kernel. + */ + + /* + * Register management/dpa devices + */ + rc = register_chrdev(DIGI_DGAP_MAJOR, "dgap", &dgap_board_fops); + if (rc < 0) + return rc; + + dgap_class = class_create(THIS_MODULE, "dgap_mgmt"); + if (IS_ERR(dgap_class)) { + rc = PTR_ERR(dgap_class); + goto failed_class; } - return ret_ptr; -} + device = device_create(dgap_class, NULL, + MKDEV(DIGI_DGAP_MAJOR, 0), + NULL, "dgap_mgmt"); + if (IS_ERR(device)) { + rc = PTR_ERR(device); + goto failed_device; + } -/* - * dgap_checknode: see if all the necessary info has been supplied for a node - * before creating the next node. - */ -static int dgap_checknode(struct cnode *p) -{ - switch (p->type) { - case LNODE: - if (p->u.line.v_speed == 0) { - pr_err("line speed not specified"); - return 1; - } - return 0; + /* Start the poller */ + spin_lock_irqsave(&dgap_poll_lock, flags); + init_timer(&dgap_poll_timer); + dgap_poll_timer.function = dgap_poll_handler; + dgap_poll_timer.data = 0; + dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick); + dgap_poll_timer.expires = dgap_poll_time; + spin_unlock_irqrestore(&dgap_poll_lock, flags); - case CNODE: - if (p->u.conc.v_speed == 0) { - pr_err("concentrator line speed not specified"); - return 1; - } - if (p->u.conc.v_nport == 0) { - pr_err("number of ports on concentrator not specified"); - return 1; - } - if (p->u.conc.v_id == 0) { - pr_err("concentrator id letter not specified"); - return 1; - } - return 0; + add_timer(&dgap_poll_timer); - case MNODE: - if (p->u.module.v_nport == 0) { - pr_err("number of ports on EBI module not specified"); - return 1; - } - if (p->u.module.v_id == 0) { - pr_err("EBI module id letter not specified"); - return 1; - } - return 0; - } - return 0; + return rc; + +failed_device: + class_destroy(dgap_class); +failed_class: + unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); + return rc; } -/* - * Given a board pointer, returns whether we should use interrupts or not. - */ -static uint dgap_config_get_useintr(struct board_t *bd) +static void dgap_stop(void) { - struct cnode *p; + unsigned long lock_flags; - if (!bd) - return 0; + spin_lock_irqsave(&dgap_poll_lock, lock_flags); + dgap_poll_stop = 1; + spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - for (p = bd->bd_config; p; p = p->next) { - if (p->type == INTRNODE) { - /* - * check for pcxr types. - */ - return p->u.useintr; - } - } + del_timer_sync(&dgap_poll_timer); - /* If not found, then don't turn on interrupts. */ - return 0; + device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0)); + class_destroy(dgap_class); + unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); } /* - * Given a board pointer, returns whether we turn on altpin or not. + * dgap_cleanup_board() + * + * Free all the memory associated with a board */ -static uint dgap_config_get_altpin(struct board_t *bd) +static void dgap_cleanup_board(struct board_t *brd) { - struct cnode *p; + unsigned int i; - if (!bd) - return 0; + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return; - for (p = bd->bd_config; p; p = p->next) { - if (p->type == ANODE) { - /* - * check for pcxr types. - */ - return p->u.altpin; - } - } + dgap_free_irq(brd); - /* If not found, then don't turn on interrupts. */ - return 0; + tasklet_kill(&brd->helper_tasklet); + + dgap_unmap(brd); + + /* Free all allocated channels structs */ + for (i = 0; i < MAXPORTS ; i++) + kfree(brd->channels[i]); + + kfree(brd->flipbuf); + kfree(brd->flipflagbuf); + + dgap_board[brd->boardnum] = NULL; + + kfree(brd); } + +/************************************************************************ + * + * Driver load/unload functions + * + ************************************************************************/ + /* - * Given a specific type of board, if found, detached link and - * returns the first occurrence in the list. + * init_module() + * + * Module load. This is where it all starts. */ -static struct cnode *dgap_find_config(int type, int bus, int slot) +static int dgap_init_module(void) { - struct cnode *p, *prev, *prev2, *found; - - p = &dgap_head; - - while (p->next) { - prev = p; - p = p->next; - - if (p->type != BNODE) - continue; + int rc; - if (p->u.board.type != type) - continue; + pr_info("%s, Digi International Part Number %s\n", DG_NAME, DG_PART); - if (p->u.board.v_pcibus && - p->u.board.pcibus != bus) - continue; + rc = dgap_start(); + if (rc) + return rc; - if (p->u.board.v_pcislot && - p->u.board.pcislot != slot) - continue; + rc = pci_register_driver(&dgap_driver); + if (rc) + goto err_stop; - found = p; - /* - * Keep walking thru the list till we - * find the next board. - */ - while (p->next) { - prev2 = p; - p = p->next; + rc = dgap_create_driver_sysfiles(&dgap_driver); + if (rc) + goto err_unregister; - if (p->type != BNODE) - continue; + dgap_driver_state = DRIVER_READY; - /* - * Mark the end of our 1 board - * chain of configs. - */ - prev2->next = NULL; + return 0; - /* - * Link the "next" board to the - * previous board, effectively - * "unlinking" our board from - * the main config. - */ - prev->next = p; +err_unregister: + pci_unregister_driver(&dgap_driver); +err_stop: + dgap_stop(); - return found; - } - /* - * It must be the last board in the list. - */ - prev->next = NULL; - return found; - } - return NULL; + return rc; } /* - * Given a board pointer, walks the config link, counting up - * all ports user specified should be on the board. - * (This does NOT mean they are all actually present right now tho) + * dgap_cleanup_module() + * + * Module unload. This is where it all ends. */ -static uint dgap_config_get_num_prts(struct board_t *bd) +static void dgap_cleanup_module(void) { - int count = 0; - struct cnode *p; + unsigned int i; + ulong lock_flags; - if (!bd) - return 0; + spin_lock_irqsave(&dgap_poll_lock, lock_flags); + dgap_poll_stop = 1; + spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - for (p = bd->bd_config; p; p = p->next) { + /* Turn off poller right away. */ + del_timer_sync(&dgap_poll_timer); - switch (p->type) { - case BNODE: - /* - * check for pcxr types. - */ - if (p->u.board.type > EPCFE) - count += p->u.board.nport; - break; - case CNODE: - count += p->u.conc.nport; - break; - case MNODE: - count += p->u.module.nport; - break; - } - } - return count; -} + dgap_remove_driver_sysfiles(&dgap_driver); -static char *dgap_create_config_string(struct board_t *bd, char *string) -{ - char *ptr = string; - struct cnode *p; - struct cnode *q; - int speed; + device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0)); + class_destroy(dgap_class); + unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); - if (!bd) { - *ptr = 0xff; - return string; + for (i = 0; i < dgap_numboards; ++i) { + dgap_remove_ports_sysfiles(dgap_board[i]); + dgap_cleanup_tty(dgap_board[i]); + dgap_cleanup_board(dgap_board[i]); } - for (p = bd->bd_config; p; p = p->next) { + dgap_cleanup_nodes(); - switch (p->type) { - case LNODE: - *ptr = '\0'; - ptr++; - *ptr = p->u.line.speed; - ptr++; - break; - case CNODE: - /* - * Because the EPC/con concentrators can have EM modules - * hanging off of them, we have to walk ahead in the - * list and keep adding the number of ports on each EM - * to the config. UGH! - */ - speed = p->u.conc.speed; - q = p->next; - if (q && (q->type == MNODE)) { - *ptr = (p->u.conc.nport + 0x80); - ptr++; - p = q; - while (q->next && (q->next->type) == MNODE) { - *ptr = (q->u.module.nport + 0x80); - ptr++; - p = q; - q = q->next; - } - *ptr = q->u.module.nport; - ptr++; - } else { - *ptr = p->u.conc.nport; - ptr++; - } + if (dgap_numboards) + pci_unregister_driver(&dgap_driver); +} - *ptr = speed; - ptr++; - break; - } - } +module_init(dgap_init_module); +module_exit(dgap_cleanup_module); - *ptr = 0xff; - return string; -} +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Digi International, http://www.digi.com"); +MODULE_DESCRIPTION("Driver for the Digi International EPCA PCI based product line"); +MODULE_SUPPORTED_DEVICE("dgap"); -- cgit v0.10.2