diff options
Diffstat (limited to 'drivers/staging/dgap/dgap.c')
-rw-r--r-- | drivers/staging/dgap/dgap.c | 7057 |
1 files changed, 0 insertions, 7057 deletions
diff --git a/drivers/staging/dgap/dgap.c b/drivers/staging/dgap/dgap.c deleted file mode 100644 index 294c1c83aa4d..000000000000 --- a/drivers/staging/dgap/dgap.c +++ /dev/null @@ -1,7057 +0,0 @@ -/* - * Copyright 2003 Digi International (www.digi.com) - * Scott H Kilau <Scott_Kilau at digi dot com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the - * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - */ - -/* - * In the original out of kernel Digi dgap driver, firmware - * loading was done via user land to driver handshaking. - * - * For cards that support a concentrator (port expander), - * I believe the concentrator its self told the card which - * concentrator is actually attached and then that info - * was used to tell user land which concentrator firmware - * image was to be downloaded. I think even the BIOS or - * FEP images required could change with the connection - * of a particular concentrator. - * - * Since I have no access to any of these cards or - * concentrators, I cannot put the correct concentrator - * firmware file names into the firmware_info structure - * as is now done for the BIOS and FEP images. - * - * I think, but am not certain, that the cards supporting - * concentrators will function without them. So support - * of these cards has been left in this driver. - * - * In order to fully support those cards, they would - * either have to be acquired for dissection or maybe - * Digi International could provide some assistance. - */ -#undef DIGI_CONCENTRATORS_SUPPORTED - -#define pr_fmt(fmt) "dgap: " fmt - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/delay.h> /* For udelay */ -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/sched.h> - -#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */ -#include <linux/ctype.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial_reg.h> -#include <linux/io.h> /* For read[bwl]/write[bwl] */ - -#include <linux/string.h> -#include <linux/device.h> -#include <linux/kdev_t.h> -#include <linux/firmware.h> - -#include "dgap.h" - -/* - * File operations permitted on Control/Management major. - */ -static const struct file_operations dgap_board_fops = { - .owner = THIS_MODULE, -}; - -static uint dgap_numboards; -static struct board_t *dgap_board[MAXBOARDS]; -static ulong dgap_poll_counter; -static int dgap_driver_state = DRIVER_INITIALIZED; -static int dgap_poll_tick = 20; /* Poll interval - 20 ms */ - -static struct class *dgap_class; - -static uint dgap_count = 500; - -/* - * Poller stuff - */ -static DEFINE_SPINLOCK(dgap_poll_lock); /* Poll scheduling lock */ -static ulong dgap_poll_time; /* Time of next poll */ -static uint dgap_poll_stop; /* Used to tell poller to stop */ -static struct timer_list dgap_poll_timer; - -/* - SUPPORTED PRODUCTS - - Card Model Number of Ports Interface - ---------------------------------------------------------------- - Acceleport Xem 4 - 64 (EIA232 & EIA422) - Acceleport Xr 4 & 8 (EIA232) - Acceleport Xr 920 4 & 8 (EIA232) - Acceleport C/X 8 - 128 (EIA232) - Acceleport EPC/X 8 - 224 (EIA232) - Acceleport Xr/422 4 & 8 (EIA422) - Acceleport 2r/920 2 (EIA232) - Acceleport 4r/920 4 (EIA232) - Acceleport 8r/920 8 (EIA232) - - IBM 8-Port Asynchronous PCI Adapter (EIA232) - IBM 128-Port Asynchronous PCI Adapter (EIA232 & EIA422) -*/ - -static struct pci_device_id dgap_pci_tbl[] = { - { DIGI_VID, PCI_DEV_XEM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { DIGI_VID, PCI_DEV_CX_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { DIGI_VID, PCI_DEV_CX_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, - { DIGI_VID, PCI_DEV_EPCJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, - { DIGI_VID, PCI_DEV_920_2_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, - { DIGI_VID, PCI_DEV_920_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, - { DIGI_VID, PCI_DEV_920_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, - { DIGI_VID, PCI_DEV_XR_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, - { DIGI_VID, PCI_DEV_XRJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, - { DIGI_VID, PCI_DEV_XR_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, - { DIGI_VID, PCI_DEV_XR_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 }, - { DIGI_VID, PCI_DEV_XR_SAIP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 }, - { DIGI_VID, PCI_DEV_XR_BULL_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 }, - { DIGI_VID, PCI_DEV_920_8_HP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 }, - { DIGI_VID, PCI_DEV_XEM_HP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 }, - {0,} /* 0 terminated list. */ -}; -MODULE_DEVICE_TABLE(pci, dgap_pci_tbl); - -/* - * A generic list of Product names, PCI Vendor ID, and PCI Device ID. - */ -struct board_id { - uint config_type; - u8 *name; - uint maxports; - uint dpatype; -}; - -static struct board_id dgap_ids[] = { - {PPCM, PCI_DEV_XEM_NAME, 64, (T_PCXM | T_PCLITE | T_PCIBUS)}, - {PCX, PCI_DEV_CX_NAME, 128, (T_CX | T_PCIBUS) }, - {PCX, PCI_DEV_CX_IBM_NAME, 128, (T_CX | T_PCIBUS) }, - {PEPC, PCI_DEV_EPCJ_NAME, 224, (T_EPC | T_PCIBUS) }, - {APORT2_920P, PCI_DEV_920_2_NAME, 2, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {APORT4_920P, PCI_DEV_920_4_NAME, 4, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {APORT8_920P, PCI_DEV_920_8_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XRJ_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_422_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_IBM_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_SAIP_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PAPORT8, PCI_DEV_XR_BULL_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {APORT8_920P, PCI_DEV_920_8_HP_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS)}, - {PPCM, PCI_DEV_XEM_HP_NAME, 64, (T_PCXM | T_PCLITE | T_PCIBUS)}, - {0,} /* 0 terminated list. */ -}; - -struct firmware_info { - u8 *conf_name; /* dgap.conf */ - u8 *bios_name; /* BIOS filename */ - u8 *fep_name; /* FEP filename */ - u8 *con_name; /* Concentrator filename FIXME*/ - int num; /* sequence number */ -}; - -/* - * Firmware - BIOS, FEP, and CONC filenames - */ -static struct firmware_info fw_info[] = { - { "dgap/dgap.conf", "dgap/sxbios.bin", "dgap/sxfep.bin", NULL, 0 }, - { "dgap/dgap.conf", "dgap/cxpbios.bin", "dgap/cxpfep.bin", NULL, 1 }, - { "dgap/dgap.conf", "dgap/cxpbios.bin", "dgap/cxpfep.bin", NULL, 2 }, - { "dgap/dgap.conf", "dgap/pcibios.bin", "dgap/pcifep.bin", NULL, 3 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 4 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 5 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 6 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 7 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 8 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 9 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 10 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 11 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 12 }, - { "dgap/dgap.conf", "dgap/xrbios.bin", "dgap/xrfep.bin", NULL, 13 }, - { "dgap/dgap.conf", "dgap/sxbios.bin", "dgap/sxfep.bin", NULL, 14 }, - {NULL,} -}; - -/* - * Default transparent print information. - */ -static struct digi_t dgap_digi_init = { - .digi_flags = DIGI_COOK, /* Flags */ - .digi_maxcps = 100, /* Max CPS */ - .digi_maxchar = 50, /* Max chars in print queue */ - .digi_bufsize = 100, /* Printer buffer size */ - .digi_onlen = 4, /* size of printer on string */ - .digi_offlen = 4, /* size of printer off string */ - .digi_onstr = "\033[5i", /* ANSI printer on string ] */ - .digi_offstr = "\033[4i", /* ANSI printer off string ] */ - .digi_term = "ansi" /* default terminal type */ -}; - -/* - * Define a local default termios struct. All ports will be created - * with this termios initially. - * - * This defines a raw port at 9600 baud, 8 data bits, no parity, - * 1 stop bit. - */ - -static struct ktermios dgap_default_termios = { - .c_iflag = (DEFAULT_IFLAGS), /* iflags */ - .c_oflag = (DEFAULT_OFLAGS), /* oflags */ - .c_cflag = (DEFAULT_CFLAGS), /* cflags */ - .c_lflag = (DEFAULT_LFLAGS), /* lflags */ - .c_cc = INIT_C_CC, - .c_line = 0, -}; - -/* - * Our needed internal static variables from dgap_parse.c - */ -static struct cnode dgap_head; -#define MAXCWORD 200 -static char dgap_cword[MAXCWORD]; - -struct toklist { - int token; - char *string; -}; - -static struct toklist dgap_brdtype[] = { - { PCX, "Digi_AccelePort_C/X_PCI" }, - { PEPC, "Digi_AccelePort_EPC/X_PCI" }, - { PPCM, "Digi_AccelePort_Xem_PCI" }, - { APORT2_920P, "Digi_AccelePort_2r_920_PCI" }, - { APORT4_920P, "Digi_AccelePort_4r_920_PCI" }, - { APORT8_920P, "Digi_AccelePort_8r_920_PCI" }, - { PAPORT4, "Digi_AccelePort_4r_PCI(EIA-232/RS-422)" }, - { PAPORT8, "Digi_AccelePort_8r_PCI(EIA-232/RS-422)" }, - { 0, NULL } -}; - -static struct toklist dgap_tlist[] = { - { BEGIN, "config_begin" }, - { END, "config_end" }, - { BOARD, "board" }, - { PCIINFO, "pciinfo" }, - { LINE, "line" }, - { CONC, "conc" }, - { CONC, "concentrator" }, - { CX, "cx" }, - { CX, "ccon" }, - { EPC, "epccon" }, - { EPC, "epc" }, - { MOD, "module" }, - { ID, "id" }, - { STARTO, "start" }, - { SPEED, "speed" }, - { CABLE, "cable" }, - { CONNECT, "connect" }, - { METHOD, "method" }, - { STATUS, "status" }, - { CUSTOM, "Custom" }, - { BASIC, "Basic" }, - { MEM, "mem" }, - { MEM, "memory" }, - { PORTS, "ports" }, - { MODEM, "modem" }, - { NPORTS, "nports" }, - { TTYN, "ttyname" }, - { CU, "cuname" }, - { PRINT, "prname" }, - { CMAJOR, "major" }, - { ALTPIN, "altpin" }, - { USEINTR, "useintr" }, - { TTSIZ, "ttysize" }, - { CHSIZ, "chsize" }, - { BSSIZ, "boardsize" }, - { UNTSIZ, "schedsize" }, - { F2SIZ, "f2200size" }, - { VPSIZ, "vpixsize" }, - { 0, NULL } -}; - -/* - * get a word from the input stream, also keep track of current line number. - * words are separated by whitespace. - */ -static char *dgap_getword(char **in) -{ - char *ret_ptr = *in; - - char *ptr = strpbrk(*in, " \t\n"); - - /* If no word found, return null */ - if (!ptr) - return NULL; - - /* Mark new location for our buffer */ - *ptr = '\0'; - *in = ptr + 1; - - /* Eat any extra spaces/tabs/newlines that might be present */ - while (*in && **in && ((**in == ' ') || - (**in == '\t') || - (**in == '\n'))) { - **in = '\0'; - *in = *in + 1; - } - - return ret_ptr; -} - - -/* - * 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; - - if (strstr(dgap_cword, "board")) { - w = dgap_getword(in); - if (!w) - return 0; - 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; - } - } - } - - return 0; -} - -/* - * 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; - - 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; - - 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; -} - -/* - * 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; - - if (!bd) - return 0; - - 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; -} - -/* - * Given a board pointer, returns whether we turn on altpin or not. - */ -static uint dgap_config_get_altpin(struct board_t *bd) -{ - struct cnode *p; - - if (!bd) - return 0; - - for (p = bd->bd_config; p; p = p->next) { - if (p->type == ANODE) { - /* - * check for pcxr types. - */ - return p->u.altpin; - } - } - - /* If not found, then don't turn on interrupts. */ - return 0; -} - -/* - * Given a specific type of board, if found, detached link and - * returns the first occurrence in the list. - */ -static struct cnode *dgap_find_config(int type, int bus, int slot) -{ - struct cnode *p, *prev, *prev2, *found; - - p = &dgap_head; - - while (p->next) { - prev = p; - p = p->next; - - if (p->type != BNODE) - continue; - - if (p->u.board.type != type) - continue; - - if (p->u.board.v_pcibus && - p->u.board.pcibus != bus) - continue; - - if (p->u.board.v_pcislot && - p->u.board.pcislot != slot) - continue; - - found = p; - /* - * Keep walking thru the list till we - * find the next board. - */ - while (p->next) { - prev2 = p; - p = p->next; - - 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; -} - -/* - * 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 uint dgap_config_get_num_prts(struct board_t *bd) -{ - int count = 0; - struct cnode *p; - - if (!bd) - return 0; - - for (p = bd->bd_config; p; p = p->next) { - 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; -} - -static char *dgap_create_config_string(struct board_t *bd, char *string) -{ - char *ptr = string; - struct cnode *p; - struct cnode *q; - int speed; - - if (!bd) { - *ptr = 0xff; - return string; - } - - for (p = bd->bd_config; p; p = p->next) { - 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++; - } - - *ptr = speed; - ptr++; - break; - } - } - - *ptr = 0xff; - return string; -} - -/* - * 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; - - p = &dgap_head; - brd = line = conc = NULL; - - /* perhaps we are adding to an existing list? */ - while (p->next) - p = p->next; - - /* file must start with a BEGIN */ - while ((rc = dgap_gettok(in)) != BEGIN) { - if (rc == 0) { - pr_err("unexpected EOF"); - return -1; - } - } - - 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 -ENOMEM; - - p = p->next; - - p->type = BNODE; - p->u.board.status = kstrdup("No", GFP_KERNEL); - line = conc = NULL; - brd = p; - linecnt = -1; - - board_type = dgap_gettok(in); - if (board_type == 0) { - pr_err("board !!type not specified"); - return -1; - } - - p->u.board.type = board_type; - - break; - - 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; - } - kfree(p->u.board.addrstr); - 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; - - 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; - } - kfree(p->u.board.pcibusstr); - 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; - } - kfree(p->u.board.pcislotstr); - 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; - - 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; - } - kfree(p->u.board.method); - p->u.board.method = kstrdup(s, GFP_KERNEL); - p->u.board.v_method = 1; - break; - - 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; - } - kfree(p->u.board.status); - p->u.board.status = kstrdup(s, GFP_KERNEL); - break; - - 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; - - case ID: /* letter ID used in tty name */ - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.board.status); - p->u.board.status = kstrdup(s, GFP_KERNEL); - - if (p->type == CNODE) { - kfree(p->u.conc.id); - p->u.conc.id = kstrdup(s, GFP_KERNEL); - p->u.conc.v_id = 1; - } else if (p->type == MNODE) { - kfree(p->u.module.id); - 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; - - 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; - - case TTYN: /* tty name prefix */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = TNODE; - - 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; - - break; - - case CU: /* cu name prefix */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = CUNODE; - - 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; - - break; - - 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; - } - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = LNODE; - conc = NULL; - line = p; - linecnt++; - break; - - case CONC: /* concentrator information */ - if (dgap_checknode(p)) - return -1; - if (!line) { - pr_err("must specify line info before concentrator"); - return -1; - } - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = CNODE; - conc = p; - - if (linecnt) - brd->u.board.conc2++; - else - brd->u.board.conc1++; - - 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; - } - - p->u.conc.type = conc_type; - - break; - - 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 -ENOMEM; - - p = p->next; - p->type = MNODE; - - if (linecnt) - brd->u.board.module2++; - else - brd->u.board.module1++; - - 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; - } - - p->u.module.type = module_type; - - break; - - case CABLE: - if (p->type == LNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.line.cable); - p->u.line.cable = kstrdup(s, GFP_KERNEL); - p->u.line.v_cable = 1; - } - break; - - 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; - - case CONNECT: - if (p->type == CNODE) { - s = dgap_getword(in); - if (!s) { - pr_err("unexpected end of file"); - return -1; - } - kfree(p->u.conc.connect); - 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; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = PNODE; - - 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; - - break; - - case CMAJOR: /* major number */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = JNODE; - - 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; - - case ALTPIN: /* altpin setting */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = ANODE; - - 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; - - case USEINTR: /* enable interrupt setting */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - 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; - - case TTSIZ: /* size of tty structure */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = TSNODE; - - 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; - - case CHSIZ: /* channel structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = CSNODE; - - 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; - - case BSSIZ: /* board structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = BSNODE; - - 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; - - case UNTSIZ: /* sched structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = USNODE; - - 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; - - case F2SIZ: /* f2200 structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - 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; - - case VPSIZ: /* vpix structure size */ - if (dgap_checknode(p)) - return -1; - - p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL); - if (!p->next) - return -ENOMEM; - - p = p->next; - p->type = VSNODE; - - 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; - } - } -} - -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.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; - } -} - -/* - * 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; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - - if (!ch->ch_bd || ch->ch_bd->magic != DGAP_BOARD_MAGIC) - return 0; - - if (!(ch->ch_bd->bd_flags & BD_FEP5PLUS)) - return 0; - - vaddr = ch->ch_bd->re_map_membase; - - if (!vaddr) - return 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; - - return readw(vaddr + offset); -} - -/* - * 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")) - goto err_req_mem; - - brd->re_map_membase = ioremap(brd->membase, 0x200000); - if (!brd->re_map_membase) - goto err_remap_mem; - - brd->re_map_port = ioremap((brd->membase + PCI_IO_OFFSET), 0x200000); - if (!brd->re_map_port) - goto err_remap_port; - - return 0; - -err_remap_port: - iounmap(brd->re_map_membase); -err_remap_mem: - release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); -err_req_mem: - release_mem_region(brd->membase, 0x200000); - - return -ENOMEM; -} - -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); -} - -/* - * 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; - - 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; - - 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; - - 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; - - case 2: - /* third character of ff sequence */ - - *cout++ = c; - - if (ch->pscan_savechar == 0x0) { - if (c == 0x0) { - ch->ch_err_break++; - *fout++ = TTY_BREAK; - } else { - ch->ch_err_parity++; - *fout++ = TTY_PARITY; - } - } - - count += 1; - ch->pscan_state = 0; - } - } - *len = count; -} - -/*======================================================================= - * - * 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); - - /* - * Figure the number of characters in the buffer. - * Exit immediately if none. - */ - - rmask = ch->ch_rsize - 1; - - 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; - } - - /* - * 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) || - !C_CREAD(tp) || - (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; - } - - /* - * 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; - } - - /* - * Ignore oruns. - */ - tmpchar = readb(&bs->orun); - if (tmpchar) { - ch->ch_err_overrun++; - writeb(0, &bs->orun); - } - - /* 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 - /* - * 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 - - /* - * 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; - } - } - - 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; - } - - 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; - - n -= s; - /* Flip queue if needed */ - tail &= rmask; - } - - writew(tail, &bs->rx_tail); - writeb(1, &bs->idata); - ch->ch_rxcount += len; - - /* - * 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); - - 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); - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - /* Tell the tty layer its okay to "eat" the data now */ - tty_flip_buffer_push(tp->port); - - if (ld) - tty_ldisc_deref(ld); -} - -static void dgap_write_wakeup(struct board_t *bd, struct channel_t *ch, - struct un_t *un, u32 mask) -{ - if (!(un->un_flags & mask)) - return; - - un->un_flags &= ~mask; - - if (!(un->un_flags & UN_ISOPEN)) - return; - - tty_wakeup(un->un_tty); - wake_up_interruptible(&un->un_flags_wait); -} - -/************************************************************************ - * Determines when CARRIER changes state and takes appropriate - * action. - ************************************************************************/ -static void dgap_carrier(struct channel_t *ch) -{ - struct board_t *bd; - - int virt_carrier = 0; - int phys_carrier = 0; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - bd = ch->ch_bd; - - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return; - - /* 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; - } - - if (ch->ch_mistat & D_CD(ch)) - phys_carrier = 1; - - if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) - virt_carrier = 1; - - if (ch->ch_c_cflag & CLOCAL) - virt_carrier = 1; - - /* - * Test for a VIRTUAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Test for a PHYSICAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - 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); - } - - /* - * 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; -} - -/*======================================================================= - * - * 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; - - if (!bd || bd->magic != DGAP_BOARD_MAGIC) - return -EIO; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - - vaddr = bd->re_map_membase; - - if (!vaddr) { - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return -EIO; - } - - eaddr = (struct ev_t __iomem *)(vaddr + EVBUF); - - /* Get our head and tail */ - head = readw(&eaddr->ev_head); - tail = readw(&eaddr->ev_tail); - - /* - * Forget it if pointers out of range. - */ - - 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; - } - - /* - * Loop to process all the events in the buffer. - */ - while (tail != head) { - /* - * Get interrupt information. - */ - - event = bd->re_map_membase + tail + EVSTART; - - port = ioread8(event); - reason = ioread8(event + 1); - modem = ioread8(event + 2); - ioread8(event + 3); - - /* - * Make sure the interrupt is valid. - */ - if (port >= bd->nasync) - goto next; - - if (!(reason & (IFMODEM | IFBREAK | IFTLW | IFTEM | IFDATA))) - goto next; - - ch = bd->channels[port]; - - 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; - } - - /* - * Process received data. - */ - if (reason & IFDATA) { - /* - * 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); - - dgap_input(ch); - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - if (ch->ch_flags & CH_RACTIVE) - ch->ch_flags |= CH_RENABLE; - else - writeb(1, &bs->idata); - - if (ch->ch_flags & CH_RWAIT) { - ch->ch_flags &= ~CH_RWAIT; - - wake_up_interruptible - (&ch->ch_tun.un_flags_wait); - } - } - - /* - * 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); - } - } - - /* - * Process Transmit low. - */ - if (reason & IFTLW) { - dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_LOW); - dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_LOW); - if (ch->ch_flags & CH_WLOW) { - ch->ch_flags &= ~CH_WLOW; - wake_up_interruptible(&ch->ch_flags_wait); - } - } - - /* - * Process Transmit empty. - */ - if (reason & IFTEM) { - dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_EMPTY); - dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_EMPTY); - if (ch->ch_flags & CH_WEMPTY) { - ch->ch_flags &= ~CH_WEMPTY; - wake_up_interruptible(&ch->ch_flags_wait); - } - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - -next: - tail = (tail + 4) & (EVMAX - EVSTART - 4); - } - - writew(tail, &eaddr->ev_tail); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return 0; -} - -/* - * Our board poller function. - */ -static void dgap_poll_tasklet(unsigned long data) -{ - struct board_t *bd = (struct board_t *)data; - ulong lock_flags; - char __iomem *vaddr; - u16 head, tail; - - if (!bd || (bd->magic != DGAP_BOARD_MAGIC)) - return; - - if (bd->inhibit_poller) - return; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - - vaddr = bd->re_map_membase; - - /* - * If board is ready, parse deeper to see if there is anything to do. - */ - if (bd->state == BOARD_READY) { - struct ev_t __iomem *eaddr; - - 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 (!bd->nasync) - goto out; - - eaddr = (struct ev_t __iomem *)(vaddr + EVBUF); - - /* Get our head and tail */ - head = readw(&eaddr->ev_head); - tail = readw(&eaddr->ev_tail); - - /* - * 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); - } - -out: - /* - * If board is doing interrupts, ACK the interrupt. - */ - if (bd->intr_running) - readb(bd->re_map_port + 2); - - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - return; - } - - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -/* - * 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; - - /* 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; - - 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; - - /* 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; - } - - 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_DGAP; - - /* - * 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); - - /* - * 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); - } - - /* 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); - - return brd; - -free_brd: - kfree(brd); - - return ERR_PTR(ret); -} - -/* - * 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; -} - -/***************************************************************************** -* -* 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; - - /* - * If board is in a failed state, don't bother - * scheduling a tasklet - */ - if (brd->state == BOARD_FAILED) { - spin_unlock(&brd->bd_lock); - continue; - } - - /* Schedule a poll helper task */ - if (!brd->intr_running) - tasklet_schedule(&brd->helper_tasklet); - - /* - * 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_cmdb - Sends a 2 byte command to the FEP. - * - * 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) -{ - char __iomem *vaddr; - struct __iomem cm_t *cm_addr; - uint count; - uint n; - u16 head; - u16 tail; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * 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; - - 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; - } - - /* - * 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); - - /* - * 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 ;;) { - head = readw(&cm_addr->cm_head); - tail = readw(&cm_addr->cm_tail); - - n = (head - tail) & (CMDMAX - CMDSTART - 4); - - if (n <= ncmds * sizeof(struct cm_t)) - break; - - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } -} - -/*======================================================================= - * - * 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; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * 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; - - 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; - } - - /* - * 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)); - - head = (head + 4) & (CMDMAX - CMDSTART - 4); - - writew(head, &cm_addr->cm_head); - - /* - * 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 ;;) { - head = readw(&cm_addr->cm_head); - tail = readw(&cm_addr->cm_tail); - - n = (head - tail) & (CMDMAX - CMDSTART - 4); - - if (n <= ncmds * sizeof(struct cm_t)) - break; - - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } -} - -/*======================================================================= - * - * 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; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * 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; - - 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; - } - - /* - * Put the data in the circular command buffer. - */ - - /* Write an FF to tell the FEP that we want an extended command */ - writeb((u8)0xff, (vaddr + head + CMDSTART + 0)); - - writeb((u8)ch->ch_portnum, (vaddr + head + CMDSTART + 1)); - writew((u16)cmd, (vaddr + head + CMDSTART + 2)); - - /* - * 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); - - writew(head, &cm_addr->cm_head); - - /* - * 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 ;;) { - head = readw(&cm_addr->cm_head); - tail = readw(&cm_addr->cm_tail); - - n = (head - tail) & (CMDMAX - CMDSTART - 4); - - if (n <= ncmds * sizeof(struct cm_t)) - break; - - if (--count == 0) { - ch->ch_bd->state = BOARD_FAILED; - return; - } - udelay(10); - } -} - -/*======================================================================= - * - * dgap_wmove - Write data to FEP buffer. - * - * ch - Pointer to channel structure. - * buf - Pointer to characters to be moved. - * cnt - Number of characters to move. - * - *=======================================================================*/ -static void dgap_wmove(struct channel_t *ch, char *buf, uint cnt) -{ - int n; - char __iomem *taddr; - struct bs_t __iomem *bs; - u16 head; - - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return; - - /* - * Check parameters. - */ - bs = ch->ch_bs; - head = readw(&bs->tx_head); - - /* - * If pointers are out of range, just return. - */ - if ((cnt > ch->ch_tsize) || - (unsigned)(head - ch->ch_tstart) >= ch->ch_tsize) - return; - - /* - * 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; - - if (cnt >= n) { - cnt -= n; - taddr = ch->ch_taddr + head; - memcpy_toio(taddr, buf, n); - head = ch->ch_tstart; - buf += n; - } - - /* - * Move rest of data. - */ - taddr = ch->ch_taddr + head; - n = cnt; - memcpy_toio(taddr, buf, n); - head += cnt; - - writew(head, &bs->tx_head); -} - -/* - * Calls the firmware to reset this channel. - */ -static void dgap_firmware_reset_port(struct channel_t *ch) -{ - dgap_cmdb(ch, CHRESET, 0, 0, 0); - - /* - * 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; -} - -/*======================================================================= - * - * 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; - - /* - * If baud rate is zero, flush queues, and set mval to drop DTR. - */ - if ((ch->ch_c_cflag & (CBAUD)) == 0) { - /* flush rx */ - head = readw(&ch->ch_bs->rx_head); - writew(head, &ch->ch_bs->rx_tail); - - /* flush tx */ - head = readw(&ch->ch_bs->tx_head); - writew(head, &ch->ch_bs->tx_tail); - - ch->ch_flags |= (CH_BAUD0); - - /* 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; - - } else if (ch->ch_custom_speed && (bd->bd_flags & BD_FEP5PLUS)) { - /* - * Tell the fep to do the command - */ - - dgap_cmdw_ext(ch, 0xff01, ch->ch_custom_speed, 0); - - /* - * 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; - - /* 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); - - } else { - /* - * Set baud rate, character size, and parity. - */ - - - int iindex = 0; - int jindex = 0; - int baud = 0; - - 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 } - }; - - /* - * 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; - - if (ch->ch_c_cflag & CBAUDEX) - iindex = 1; - - if (ch->ch_digi.digi_flags & DIGI_FAST) - iindex += 2; - - jindex = baud; - - if ((iindex >= 0) && (iindex < 4) && - (jindex >= 0) && (jindex < 16)) - baud = bauds[iindex][jindex]; - else - baud = 0; - - if (baud == 0) - baud = 9600; - - ch->ch_baud_info = baud; - - /* - * 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); - - /* - * 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; - - 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; - - /* - * 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; - } - - if (baudpart) - cflag = (cflag & ~(CBAUD | CBAUDEX)) | baudpart; - } - - 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); - } - - /* 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. - */ - 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); - } - - /* - * 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); - } - - if (ch->ch_digi.digi_flags & DIGI_ALTPIN) - iflag |= IALTPIN; - - if (iflag != ch->ch_fepiflag) { - ch->ch_fepiflag = iflag; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdw(ch, SIFLAG, (u16)ch->ch_fepiflag, 0); - } - - /* - * Select hardware handshaking. - */ - hflow = 0; - - 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); - - if (hflow != ch->ch_hflow) { - ch->ch_hflow = hflow; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SHFLOW, (u8)hflow, 0xff, 0); - } - - /* - * Set RTS and/or DTR Toggle if needed, - * but only if product is FEP5+ based. - */ - if (bd->bd_flags & BD_FEP5PLUS) { - u16 hflow2 = 0; - - 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_ext(ch, 0xff03, hflow2, 0); - } - - /* - * Set modem control lines. - */ - - mval ^= ch->ch_mforce & (mval ^ ch->ch_mval); - - if (ch->ch_mostat ^ mval) { - ch->ch_mostat = mval; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SMODEM, (u8)mval, D_RTS(ch) | D_DTR(ch), 0); - } - - /* - * Read modem signals, and then call carrier function. - */ - ch->ch_mistat = readb(&ch->ch_bs->m_stat); - dgap_carrier(ch); - - /* - * 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; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SFLOWC, ch->ch_fepstartc, ch->ch_fepstopc, 0); - } - - /* - * 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; - - /* Okay to have channel and board locks held calling this */ - dgap_cmdb(ch, SAFLOWC, ch->ch_fepastartc, ch->ch_fepastopc, 0); - } - - return 0; -} - -/* - * 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) -{ - int retval = 0; - struct un_t *un; - ulong lock_flags; - uint old_flags; - int sleep_on_un_flags; - - 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 -EIO; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - ch->ch_wopen++; - - /* Loop forever */ - while (1) { - sleep_on_un_flags = 0; - - /* - * If board has failed somehow during our sleep, - * bail with error. - */ - if (ch->ch_bd->state == BOARD_FAILED) { - retval = -EIO; - break; - } - - /* If tty was hung up, break out of loop and set error. */ - if (tty_hung_up_p(file)) { - retval = -EAGAIN; - break; - } - - /* - * 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)) { - /* - * 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 (file->f_flags & O_NONBLOCK) - break; - - if (tty->flags & (1 << TTY_IO_ERROR)) - break; - - if (ch->ch_flags & CH_CD) - break; - - if (ch->ch_flags & CH_FCAR) - break; - } else { - sleep_on_un_flags = 1; - } - - /* - * 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; - } - - /* - * 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; - - /* - * Let go of channel lock before calling schedule. - * Our poller will get any FEP events and wake us up when DCD - * eventually goes active. - */ - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* - * 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)); - } - - /* - * We got woken up for some reason. - * Before looping around, grab our channel lock. - */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - } - - ch->ch_wopen--; - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - return retval; -} - -/* - * dgap_tty_flush_buffer() - * - * Flush Tx buffer (make in == out) - */ -static void dgap_tty_flush_buffer(struct tty_struct *tty) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - ulong lock_flags2; - u16 head; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - 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); - - 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); - } - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - tty_wakeup(tty); -} - -/* - * 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) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - 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; - - /* flush the transmit queues */ - dgap_tty_flush_buffer(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 (!tty) - return 0; - - un = tty->driver_data; - 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; - - bs = ch->ch_bs; - if (!bs) - return 0; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - tmask = (ch->ch_tsize - 1); - - /* Get Transmit queue pointers */ - thead = readw(&bs->tx_head) & tmask; - ttail = readw(&bs->tx_tail) & tmask; - - /* Get tbusy flag */ - tbusy = readb(&bs->tbusy); - - /* 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); - - /* - * 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). - */ - - 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. - */ - 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; - } - } - - return chars; -} - -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; - - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - bs = ch->ch_bs; - if (!bs) - return -EIO; - - /* Loop until data is drained */ - while (count != 0) { - count = dgap_tty_chars_in_buffer(tty); - - if (count == 0) - break; - - /* 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); - - /* 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; - } - - 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. - */ - if (un->un_type != DGAP_PRINT) - return bytes_available; - - 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; - - 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; - } - - bytes_available = min(cps_limit, bytes_available); - } - - return bytes_available; -} - -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; - - 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); - } - } -} - -/* - * 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; - - if (!tty) - return 0; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - - bs = ch->ch_bs; - if (!bs) - return 0; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - tmask = ch->ch_tsize - 1; - head = readw(&bs->tx_head) & tmask; - tail = readw(&bs->tx_tail) & tmask; - - ret = tail - head - 1; - if (ret < 0) - ret += ch->ch_tsize; - - /* Limit printer to maxcps */ - ret = dgap_maxcps_room(ch, un, ret); - - /* - * 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; - } - - if (ret < 0) - ret = 0; - - /* - * 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); - - return ret; -} - -/* - * 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; - - if (!tty) - return 0; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return 0; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return 0; - - bs = ch->ch_bs; - if (!bs) - return 0; - - if (!count) - return 0; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - /* 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; - - 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); - - /* - * 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; - } - - /* - * 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; - } - - /* - * 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; - } - - n = count; - - /* - * 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; - - if (n >= remain) { - n -= remain; - vaddr = ch->ch_taddr + head; - - memcpy_toio(vaddr, (u8 *)buf, remain); - - head = ch->ch_tstart; - buf += remain; - } - - if (n > 0) { - /* - * Move rest of data. - */ - vaddr = ch->ch_taddr + head; - remain = n; - - memcpy_toio(vaddr, (u8 *)buf, remain); - head += remain; - } - - if (count) { - ch->ch_txcount += count; - head &= tmask; - writew(head, &bs->tx_head); - } - - dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); - - /* - * 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 (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; -} - -/* - * 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 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; - - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - 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 result; -} - -/* - * dgap_tty_tiocmset() - * - * Set modem signals, called by ld. - */ -static int dgap_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - 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 -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - bd = ch->ch_bd; - 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); - - if (set & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } - - 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; -} - -/* - * 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; - - if (!tty || tty->magic != TTY_MAGIC) - return -EIO; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EIO; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EIO; - - 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; - } - - 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); - - return 0; -} - -/* - * 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() - * - * 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; - - 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); - - /* - * 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... - */ -#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 - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -/* - * 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; - - 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 put_user(result, value); -} - -/* - * 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; - - ret = get_user(arg, value); - if (ret) - return ret; - - switch (command) { - case TIOCMBIS: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval |= D_RTS(ch); - } - - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval |= D_DTR(ch); - } - - break; - - case TIOCMBIC: - if (arg & TIOCM_RTS) { - ch->ch_mforce |= D_RTS(ch); - ch->ch_mval &= ~(D_RTS(ch)); - } - - if (arg & TIOCM_DTR) { - ch->ch_mforce |= D_DTR(ch); - ch->ch_mval &= ~(D_DTR(ch)); - } - - break; - - case TIOCMSET: - ch->ch_mforce = D_DTR(ch) | D_RTS(ch); - - if (arg & TIOCM_RTS) - ch->ch_mval |= D_RTS(ch); - else - ch->ch_mval &= ~(D_RTS(ch)); - - if (arg & TIOCM_DTR) - ch->ch_mval |= (D_DTR(ch)); - else - ch->ch_mval &= ~(D_DTR(ch)); - - break; - - default: - return -EINVAL; - } - - 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; -} - -/* - * 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; - - if (!retinfo) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -/* - * 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 (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; - - if (ch->ch_digi.digi_maxchar < 1) - ch->ch_digi.digi_maxchar = 1; - - 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() - * - * Ioctl to get the current edelay setting. - * - * - * - */ -static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo) -{ - 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; - - un = tty->driver_data; - if (!un || un->magic != DGAP_UNIT_MAGIC) - return -EFAULT; - - ch = un->un_ch; - if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - tmp = readw(&ch->ch_bs->edelay); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -/* - * 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; - - if (copy_from_user(&new_digi, new_info, sizeof(int))) - return -EFAULT; - - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - writew((u16)new_digi, &ch->ch_bs->edelay); - - 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_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; - - 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() - * - * 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 (copy_from_user(&new_rate, new_info, sizeof(unsigned int))) - return -EFAULT; - - if (bd->bd_flags & BD_FEP5PLUS) { - spin_lock_irqsave(&bd->bd_lock, lock_flags); - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - - ch->ch_custom_speed = new_rate; - - 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_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; - - 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); - - 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]; - - dgap_carrier(ch); - dgap_param(ch, bd, un->un_type); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -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; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - 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); - - ch->ch_flags |= (CH_RXBLOCK); -#if 1 - dgap_cmdw(ch, RPAUSE, 0, 0); -#endif - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -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; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - 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); - - 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); -} - -static struct board_t *find_board_by_major(unsigned int major) -{ - unsigned int i; - - for (i = 0; i < MAXBOARDS; i++) { - struct board_t *brd = dgap_board[i]; - - if (!brd) - return NULL; - if (major == brd->serial_driver->major || - major == brd->print_driver->major) - return brd; - } - - return NULL; -} - -/************************************************************************ - * - * TTY Entry points and helper functions - * - ************************************************************************/ - -/* - * 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; - - major = MAJOR(tty_devnum(tty)); - minor = MINOR(tty_devnum(tty)); - - brd = find_board_by_major(major); - if (!brd) - return -EIO; - - /* - * 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)); - - 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->serial_driver->major) { - un = &brd->channels[minor]->ch_tun; - un->un_type = DGAP_SERIAL; - } else if (major == brd->print_driver->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; - - /* - * 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; - } - - /* - * Initialize tty's - */ - if (!(un->un_flags & UN_ISOPEN)) { - /* Store important variables. */ - un->un_tty = tty; - - /* Maybe do something here to the TTY struct as well? */ - } - - /* - * Initialize if neither terminal or printer is open. - */ - if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { - ch->ch_mforce = 0; - ch->ch_mval = 0; - - /* - * Flush input queue. - */ - head = readw(&bs->rx_head); - writew(head, &bs->rx_tail); - - ch->ch_flags = 0; - ch->pscan_state = 0; - ch->pscan_savechar = 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]; - - /* TODO: flush our TTY struct here? */ - } - - dgap_carrier(ch); - /* - * Run param in case we changed anything - */ - dgap_param(ch, brd, un->un_type); - - /* - * follow protocol for opening port - */ - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&brd->bd_lock, lock_flags); - - rc = dgap_block_til_ready(tty, file, ch); - - 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_tty_close() - * - */ -static void dgap_tty_close(struct tty_struct *tty, struct file *file) -{ - struct board_t *bd; - struct channel_t *ch; - struct un_t *un; - ulong lock_flags; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - 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(&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)) { - /* - * 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. - */ - un->un_open_count = 1; - } - - 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); - return; - } - - /* OK, its the last close on the unit */ - - un->un_flags |= UN_CLOSING; - - 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)) { - 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 */ - - dgap_wait_for_drain(tty); - - dgap_tty_flush_buffer(tty); - tty_ldisc_flush(tty); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - tty->closing = 0; - - /* - * 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); - - /* .25 second delay for dropping RTS/DTR */ - schedule_timeout_interruptible(msecs_to_jiffies(250)); - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - } - - ch->pscan_state = 0; - ch->pscan_savechar = 0; - ch->ch_baud_info = 0; - } - - /* - * 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; - } - - 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_flags_wait); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); -} - -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; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - 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, RESUMETX, 0, 0); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); -} - -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 || tty->magic != TTY_MAGIC) - return; - - 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; - - if (!tty || tty->magic != TTY_MAGIC) - return; - - 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); - - /* TODO: Do something here */ - - 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_tty_ioctl() - * - * The usual assortment of ioctl's - */ -static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - 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; - - 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 -ENODEV; - - 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 */ - 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; - - 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); - - if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) - 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 TCSBRKP: - /* support for POSIX tcsendbreak() - - * 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; - - 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; - - 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 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; - - case TIOCGSOFTCAR: - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long __user *)arg); - - case TIOCSSOFTCAR: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - 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); - - /* - * Here are any additional ioctl's that we want to implement - */ - - 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 ((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); - } - 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: - /* - * 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); - } - - /* 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; - - /* pretend we didn't recognize this */ - return -ENOIOCTLCMD; - - 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: - /* - * 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; - } - - switch (arg) { - 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; - } - - 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); - - 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); - - 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); - - 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; - - default: - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - spin_unlock_irqrestore(&bd->bd_lock, lock_flags); - - return -ENOIOCTLCMD; - } -} - -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 -}; - -/************************************************************************ - * - * 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, - TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - 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; - - /* - * 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, - TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | - TTY_DRIVER_HARDWARE_BREAK); - 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; - - /* - * 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; - - 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); -} - -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; - - brd->flipflagbuf = kmalloc(MYFLIPLEN, GFP_KERNEL); - if (!brd->flipflagbuf) { - kfree(brd->flipbuf); - return -ENOMEM; - } - - return 0; -} - -static void dgap_free_flipbuf(struct board_t *brd) -{ - kfree(brd->flipbuf); - kfree(brd->flipflagbuf); -} - -static struct board_t *dgap_verify_board(struct device *p) -{ - struct board_t *bd; - - if (!p) - return NULL; - - bd = dev_get_drvdata(p); - if (!bd || bd->magic != DGAP_BOARD_MAGIC || bd->state != BOARD_READY) - return NULL; - - return bd; -} - -static ssize_t dgap_ports_state_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) { - count += snprintf(buf + count, PAGE_SIZE - count, - "%d %s\n", bd->channels[i]->ch_portnum, - bd->channels[i]->ch_open_count ? "Open" : "Closed"); - } - return count; -} -static DEVICE_ATTR(ports_state, S_IRUSR, dgap_ports_state_show, NULL); - -static ssize_t dgap_ports_baud_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) { - count += snprintf(buf + count, PAGE_SIZE - count, "%d %d\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_baud_info); - } - return count; -} -static DEVICE_ATTR(ports_baud, S_IRUSR, dgap_ports_baud_show, NULL); - -static ssize_t dgap_ports_msignals_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) { - if (bd->channels[i]->ch_open_count) - count += snprintf(buf + count, PAGE_SIZE - count, - "%d %s %s %s %s %s %s\n", - bd->channels[i]->ch_portnum, - (bd->channels[i]->ch_mostat & - UART_MCR_RTS) ? "RTS" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_CTS) ? "CTS" : "", - (bd->channels[i]->ch_mostat & - UART_MCR_DTR) ? "DTR" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_DSR) ? "DSR" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_DCD) ? "DCD" : "", - (bd->channels[i]->ch_mistat & - UART_MSR_RI) ? "RI" : ""); - else - count += snprintf(buf + count, PAGE_SIZE - count, - "%d\n", bd->channels[i]->ch_portnum); - } - return count; -} -static DEVICE_ATTR(ports_msignals, S_IRUSR, dgap_ports_msignals_show, NULL); - -static ssize_t dgap_ports_iflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_iflag); - return count; -} -static DEVICE_ATTR(ports_iflag, S_IRUSR, dgap_ports_iflag_show, NULL); - -static ssize_t dgap_ports_cflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_cflag); - return count; -} -static DEVICE_ATTR(ports_cflag, S_IRUSR, dgap_ports_cflag_show, NULL); - -static ssize_t dgap_ports_oflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_oflag); - return count; -} -static DEVICE_ATTR(ports_oflag, S_IRUSR, dgap_ports_oflag_show, NULL); - -static ssize_t dgap_ports_lflag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_c_lflag); - return count; -} -static DEVICE_ATTR(ports_lflag, S_IRUSR, dgap_ports_lflag_show, NULL); - -static ssize_t dgap_ports_digi_flag_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_digi.digi_flags); - return count; -} -static DEVICE_ATTR(ports_digi_flag, S_IRUSR, dgap_ports_digi_flag_show, NULL); - -static ssize_t dgap_ports_rxcount_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_rxcount); - return count; -} -static DEVICE_ATTR(ports_rxcount, S_IRUSR, dgap_ports_rxcount_show, NULL); - -static ssize_t dgap_ports_txcount_show(struct device *p, - struct device_attribute *attr, - char *buf) -{ - struct board_t *bd; - int count = 0; - unsigned int i; - - bd = dgap_verify_board(p); - if (!bd) - return 0; - - for (i = 0; i < bd->nasync; i++) - count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n", - bd->channels[i]->ch_portnum, - bd->channels[i]->ch_txcount); - return count; -} -static DEVICE_ATTR(ports_txcount, S_IRUSR, dgap_ports_txcount_show, NULL); - -static ssize_t dgap_tty_state_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, "%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; - - 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_rxcount_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, "%ld\n", ch->ch_rxcount); -} -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) -{ - 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, "%ld\n", ch->ch_txcount); -} -static DEVICE_ATTR(txcount, S_IRUSR, dgap_tty_txcount_show, NULL); - -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; - 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; - - 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 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; -} - -/* - * 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) -{ - u8 __iomem *addr; - uint offset; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; - - addr = brd->re_map_membase; - - /* - * Download FEP - */ - offset = 0x1000; - memcpy_toio(addr + offset, ufep, len); - - /* - * 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; - - xconfig = dgap_create_config_string(brd, string); - - /* Write string to board memory */ - config = addr + CONFIG; - for (; i < CONFIGSIZE; i++, config++, xconfig++) { - writeb(*xconfig, config); - if ((*xconfig & 0xff) == 0xff) - break; - } - } - - writel(0xbfc01004, (addr + 0xc34)); - writel(0x3, (addr + 0xc30)); -} - -/* - * Waits for the FEP to report thats its ready for us to use. - */ -static int dgap_test_fep(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 + FEPSTAT); - - /* - * 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; - - return 0; - } - msleep_interruptible(10); - brd->wait_for_fep++; - word = readw(addr + FEPSTAT); - } - - /* 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; -} - -/* - * Physically forces the FEP5 card to reset itself. - */ -static void dgap_do_reset_board(struct board_t *brd) -{ - u8 check; - u32 check1; - u32 check2; - unsigned int i; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || - !brd->re_map_membase || !brd->re_map_port) - return; - - /* 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; - } -} - -#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) -{ - char __iomem *vaddr; - u16 offset; - struct downld_t *to_dp; - - if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) - return; - - 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; -} -#endif - -#define EXPANSION_ROM_SIZE (64 * 1024) -#define FEP5_ROM_MAGIC (0xFEFFFFFF) - -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; - - /* - * 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); - - /* VPD not supported, bail */ - if (!magic) - return; - - /* - * 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); - - byte1 = readb(brd->re_map_membase); - byte2 = readb(brd->re_map_membase + 1); - - /* - * If the board correctly swapped to the OTPROM memory, - * the first 2 bytes (header) should be 0x55, 0xAA - */ - if (byte1 == 0x55 && byte2 == 0xAA) { - base_offset = 0; - - /* - * We have to run through all the OTPROM memory looking - * for the VPD offset. - */ - while (base_offset <= EXPANSION_ROM_SIZE) { - /* - * 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); - - /* 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); - } - } - } - - /* - * 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); -} - - -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); - - -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); - - -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); - - -static int dgap_create_driver_sysfiles(struct pci_driver *dgap_driver) -{ - int rc = 0; - struct device_driver *driverfs = &dgap_driver->driver; - - 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 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, - .attrs = dgap_sysfs_tty_entries, -}; - -static void dgap_create_tty_sysfs(struct un_t *un, struct device *c) -{ - int ret; - - ret = sysfs_create_group(&c->kobj, &dgap_tty_attribute_group); - if (ret) - return; - - dev_set_drvdata(c, un); -} - -static void dgap_remove_tty_sysfs(struct device *c) -{ - sysfs_remove_group(&c->kobj, &dgap_tty_attribute_group); -} - -/* - * Create pr and tty device entries - */ -static int dgap_tty_register_ports(struct board_t *brd) -{ - struct channel_t *ch; - int i; - int ret; - - brd->serial_ports = kcalloc(brd->nasync, sizeof(*brd->serial_ports), - GFP_KERNEL); - if (!brd->serial_ports) - return -ENOMEM; - - brd->printer_ports = kcalloc(brd->nasync, sizeof(*brd->printer_ports), - GFP_KERNEL); - if (!brd->printer_ports) { - ret = -ENOMEM; - goto free_serial_ports; - } - - for (i = 0; i < brd->nasync; i++) { - tty_port_init(&brd->serial_ports[i]); - tty_port_init(&brd->printer_ports[i]); - } - - 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; - } - - dgap_create_tty_sysfs(&ch->ch_tun, classp); - ch->ch_tun.un_sysfs = classp; - - classp = tty_port_register_device(&brd->printer_ports[i], - brd->print_driver, - i, NULL); - - 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; - } - dgap_create_ports_sysfiles(brd); - - return 0; - -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 (ch->ch_pun.un_sysfs) { - dgap_remove_tty_sysfs(ch->ch_pun.un_sysfs); - tty_unregister_device(brd->print_driver, i); - } - i--; - } - - for (i = 0; i < brd->nasync; i++) { - tty_port_destroy(&brd->serial_ports[i]); - tty_port_destroy(&brd->printer_ports[i]); - } - - kfree(brd->printer_ports); - brd->printer_ports = NULL; - -free_serial_ports: - kfree(brd->serial_ports); - brd->serial_ports = NULL; - - return ret; -} - -/* - * 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; - - 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); - - 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); -} - -static int dgap_request_irq(struct board_t *brd) -{ - int rc; - - 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) { - rc = request_irq(brd->irq, dgap_intr, IRQF_SHARED, "DGAP", brd); - - if (!rc) - brd->intr_used = 1; - } - return 0; -} - -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; - } - - dgap_config_buf = kzalloc(fw->size + 1, GFP_KERNEL); - if (!dgap_config_buf) { - release_firmware(fw); - return -ENOMEM; - } - - memcpy(dgap_config_buf, fw->data, fw->size); - release_firmware(fw); - - /* - * preserve dgap_config_buf - * as dgap_parsefile would - * otherwise alter it. - */ - tmp_ptr = dgap_config_buf; - - if (dgap_parsefile(&tmp_ptr) != 0) { - kfree(dgap_config_buf); - return -EINVAL; - } - kfree(dgap_config_buf); - } - - /* - * 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); - - /* - * 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); - - if (!brd->bd_config) { - dev_err(&pdev->dev, "No valid configuration found\n"); - return -EINVAL; - } - - 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; -} - -/* - * 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]); -} - -static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int rc; - struct board_t *brd; - - if (dgap_numboards >= MAXBOARDS) - return -EPERM; - - 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; - - /* - * Do tty device initialization. - */ - rc = dgap_tty_init(brd); - if (rc < 0) - goto free_irq; - - rc = dgap_tty_register_ports(brd); - if (rc) - goto tty_free; - - brd->state = BOARD_READY; - brd->dpastatus = BD_RUNNING; - - dgap_board[dgap_numboards++] = brd; - - 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; -} - -/* - * dgap_cleanup_board() - * - * Free all the memory associated with a board - */ -static void dgap_cleanup_board(struct board_t *brd) -{ - unsigned int i; - - if (!brd || brd->magic != DGAP_BOARD_MAGIC) - return; - - dgap_free_irq(brd); - - 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); -} - -static void dgap_stop(bool removesys, struct pci_driver *drv) -{ - unsigned long lock_flags; - - spin_lock_irqsave(&dgap_poll_lock, lock_flags); - dgap_poll_stop = 1; - spin_unlock_irqrestore(&dgap_poll_lock, lock_flags); - - del_timer_sync(&dgap_poll_timer); - if (removesys) - dgap_remove_driver_sysfiles(drv); - - device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0)); - class_destroy(dgap_class); - unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); -} - -static void dgap_remove_one(struct pci_dev *dev) -{ - unsigned int i; - struct pci_driver *drv = to_pci_driver(dev->dev.driver); - - dgap_stop(true, drv); - 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]); - } - - dgap_cleanup_nodes(); -} - -static struct pci_driver dgap_driver = { - .name = "dgap", - .probe = dgap_init_one, - .id_table = dgap_pci_tbl, - .remove = dgap_remove_one, -}; - -/* - * Start of driver. - */ -static int dgap_start(void) -{ - int rc; - unsigned long flags; - struct device *device; - - dgap_numboards = 0; - - pr_info("For the tools package please visit http://www.digi.com\n"); - - /* - * 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; - } - - 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; - } - - /* Start the poller */ - spin_lock_irqsave(&dgap_poll_lock, flags); - setup_timer(&dgap_poll_timer, dgap_poll_handler, 0); - 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; -} - -/************************************************************************ - * - * Driver load/unload functions - * - ************************************************************************/ - -/* - * init_module() - * - * Module load. This is where it all starts. - */ -static int dgap_init_module(void) -{ - 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) { - dgap_stop(false, NULL); - return rc; - } - - rc = dgap_create_driver_sysfiles(&dgap_driver); - if (rc) - goto err_unregister; - - dgap_driver_state = DRIVER_READY; - - return 0; - -err_unregister: - pci_unregister_driver(&dgap_driver); - return rc; -} - -/* - * dgap_cleanup_module() - * - * Module unload. This is where it all ends. - */ -static void dgap_cleanup_module(void) -{ - if (dgap_numboards) - pci_unregister_driver(&dgap_driver); -} - -module_init(dgap_init_module); -module_exit(dgap_cleanup_module); - -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"); |