From c84b8b503eaccf5c9e7b9f78553cbed8e97a9230 Mon Sep 17 00:00:00 2001 From: "Scott_Kilau@digi.com" Date: Wed, 21 Aug 2013 21:48:31 -0400 Subject: [PATCH] staging: dgap: adds dgap driver to staging This patch adds the dgap driver to staging. This is a TTY serial port driver for the EPCA PCI based product line by Digi International . Signed-off-by: Lidza Louina Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgap/Kconfig | 6 + drivers/staging/dgap/Makefile | 9 + drivers/staging/dgap/dgap_conf.h | 290 +++ drivers/staging/dgap/dgap_downld.h | 69 + drivers/staging/dgap/dgap_driver.c | 1063 ++++++++ drivers/staging/dgap/dgap_driver.h | 626 +++++ drivers/staging/dgap/dgap_fep5.c | 1957 ++++++++++++++ drivers/staging/dgap/dgap_fep5.h | 253 ++ drivers/staging/dgap/dgap_kcompat.h | 93 + drivers/staging/dgap/dgap_mgmt.c | 768 ++++++ drivers/staging/dgap/dgap_mgmt.h | 38 + drivers/staging/dgap/dgap_parse.c | 1372 ++++++++++ drivers/staging/dgap/dgap_parse.h | 35 + drivers/staging/dgap/dgap_pci.h | 92 + drivers/staging/dgap/dgap_proc.c | 1924 ++++++++++++++ drivers/staging/dgap/dgap_proc.h | 151 ++ drivers/staging/dgap/dgap_sysfs.c | 796 ++++++ drivers/staging/dgap/dgap_sysfs.h | 48 + drivers/staging/dgap/dgap_trace.c | 186 ++ drivers/staging/dgap/dgap_trace.h | 36 + drivers/staging/dgap/dgap_tty.c | 3651 +++++++++++++++++++++++++++ drivers/staging/dgap/dgap_tty.h | 39 + drivers/staging/dgap/dgap_types.h | 36 + drivers/staging/dgap/digi.h | 376 +++ drivers/staging/dgap/downld.c | 798 ++++++ 25 files changed, 14712 insertions(+) create mode 100644 drivers/staging/dgap/Kconfig create mode 100644 drivers/staging/dgap/Makefile create mode 100644 drivers/staging/dgap/dgap_conf.h create mode 100644 drivers/staging/dgap/dgap_downld.h create mode 100644 drivers/staging/dgap/dgap_driver.c create mode 100644 drivers/staging/dgap/dgap_driver.h create mode 100644 drivers/staging/dgap/dgap_fep5.c create mode 100644 drivers/staging/dgap/dgap_fep5.h create mode 100644 drivers/staging/dgap/dgap_kcompat.h create mode 100644 drivers/staging/dgap/dgap_mgmt.c create mode 100644 drivers/staging/dgap/dgap_mgmt.h create mode 100644 drivers/staging/dgap/dgap_parse.c create mode 100644 drivers/staging/dgap/dgap_parse.h create mode 100644 drivers/staging/dgap/dgap_pci.h create mode 100644 drivers/staging/dgap/dgap_proc.c create mode 100644 drivers/staging/dgap/dgap_proc.h create mode 100644 drivers/staging/dgap/dgap_sysfs.c create mode 100644 drivers/staging/dgap/dgap_sysfs.h create mode 100644 drivers/staging/dgap/dgap_trace.c create mode 100644 drivers/staging/dgap/dgap_trace.h create mode 100644 drivers/staging/dgap/dgap_tty.c create mode 100644 drivers/staging/dgap/dgap_tty.h create mode 100644 drivers/staging/dgap/dgap_types.h create mode 100644 drivers/staging/dgap/digi.h create mode 100644 drivers/staging/dgap/downld.c diff --git a/drivers/staging/dgap/Kconfig b/drivers/staging/dgap/Kconfig new file mode 100644 index 000000000000..31f1d7533eec --- /dev/null +++ b/drivers/staging/dgap/Kconfig @@ -0,0 +1,6 @@ +config DGAP + tristate "Digi EPCA PCI products" + default n + depends on TTY + ---help--- + Driver for the Digi International EPCA PCI based product line diff --git a/drivers/staging/dgap/Makefile b/drivers/staging/dgap/Makefile new file mode 100644 index 000000000000..0aa5cde2c823 --- /dev/null +++ b/drivers/staging/dgap/Makefile @@ -0,0 +1,9 @@ +EXTRA_CFLAGS += -DDG_NAME=\"dgap-1.3-16\" -DDG_PART=\"40002347_C\" + +obj-$(CONFIG_DGAP) += dgap.o + + +dgap-objs := dgap_driver.o dgap_fep5.o dgap_mgmt.o \ + dgap_parse.o dgap_proc.o dgap_trace.o \ + dgap_tty.o dgap_sysfs.o + diff --git a/drivers/staging/dgap/dgap_conf.h b/drivers/staging/dgap/dgap_conf.h new file mode 100644 index 000000000000..88097013ed04 --- /dev/null +++ b/drivers/staging/dgap/dgap_conf.h @@ -0,0 +1,290 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ***************************************************************************** + * + * dgap_conf.h - Header file for installations and parse files. + * + * $Id: dgap_conf.h,v 1.1 2009/10/23 14:01:57 markh Exp $ + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef _DGAP_CONF_H +#define _DGAP_CONF_H + +#define NULLNODE 0 /* header node, not used */ +#define BNODE 1 /* Board node */ +#define LNODE 2 /* Line node */ +#define CNODE 3 /* Concentrator node */ +#define MNODE 4 /* EBI Module node */ +#define TNODE 5 /* tty name prefix node */ +#define CUNODE 6 /* cu name prefix (non-SCO) */ +#define PNODE 7 /* trans. print prefix node */ +#define JNODE 8 /* maJor number node */ +#define ANODE 9 /* altpin */ +#define TSNODE 10 /* tty structure size */ +#define CSNODE 11 /* channel structure size */ +#define BSNODE 12 /* board structure size */ +#define USNODE 13 /* unit schedule structure size */ +#define FSNODE 14 /* f2200 structure size */ +#define VSNODE 15 /* size of VPIX structures */ +#define INTRNODE 16 /* enable interrupt */ + +/* Enumeration of tokens */ +#define BEGIN 1 +#define END 2 +#define BOARD 10 + +#define EPCFS 11 /* start of EPC family definitions */ +#define ICX 11 +#define MCX 13 +#define PCX 14 +#define IEPC 15 +#define EEPC 16 +#define MEPC 17 +#define IPCM 18 +#define EPCM 19 +#define MPCM 20 +#define PEPC 21 +#define PPCM 22 +#ifdef CP +#define ICP 23 +#define ECP 24 +#define MCP 25 +#endif +#define EPCFE 25 /* end of EPC family definitions */ +#define PC2E 26 +#define PC4E 27 +#define PC4E8K 28 +#define PC8E 29 +#define PC8E8K 30 +#define PC16E 31 +#define MC2E8K 34 +#define MC4E8K 35 +#define MC8E8K 36 + +#define AVANFS 42 /* start of Avanstar family definitions */ +#define A8P 42 +#define A16P 43 +#define AVANFE 43 /* end of Avanstar family definitions */ + +#define DA2000FS 44 /* start of AccelePort 2000 family definitions */ +#define DA22 44 /* AccelePort 2002 */ +#define DA24 45 /* AccelePort 2004 */ +#define DA28 46 /* AccelePort 2008 */ +#define DA216 47 /* AccelePort 2016 */ +#define DAR4 48 /* AccelePort RAS 4 port */ +#define DAR8 49 /* AccelePort RAS 8 port */ +#define DDR24 50 /* DataFire RAS 24 port */ +#define DDR30 51 /* DataFire RAS 30 port */ +#define DDR48 52 /* DataFire RAS 48 port */ +#define DDR60 53 /* DataFire RAS 60 port */ +#define DA2000FE 53 /* end of AccelePort 2000/RAS family definitions */ + +#define PCXRFS 106 /* start of PCXR family definitions */ +#define APORT4 106 +#define APORT8 107 +#define PAPORT4 108 +#define PAPORT8 109 +#define APORT4_920I 110 +#define APORT8_920I 111 +#define APORT4_920P 112 +#define APORT8_920P 113 +#define APORT2_920P 114 +#define PCXRFE 117 /* end of PCXR family definitions */ + +#define LINE 82 +#ifdef T1 +#define T1M 83 +#define E1M 84 +#endif +#define CONC 64 +#define CX 65 +#define EPC 66 +#define MOD 67 +#define PORTS 68 +#define METHOD 69 +#define CUSTOM 70 +#define BASIC 71 +#define STATUS 72 +#define MODEM 73 +/* The following tokens can appear in multiple places */ +#define SPEED 74 +#define NPORTS 75 +#define ID 76 +#define CABLE 77 +#define CONNECT 78 +#define IO 79 +#define MEM 80 +#define DPSZ 81 + +#define TTYN 90 +#define CU 91 +#define PRINT 92 +#define XPRINT 93 +#define CMAJOR 94 +#define ALTPIN 95 +#define STARTO 96 +#define USEINTR 97 +#define PCIINFO 98 + +#define TTSIZ 100 +#define CHSIZ 101 +#define BSSIZ 102 +#define UNTSIZ 103 +#define F2SIZ 104 +#define VPSIZ 105 + +#define TOTAL_BOARD 2 +#define CURRENT_BRD 4 +#define BOARD_TYPE 6 +#define IO_ADDRESS 8 +#define MEM_ADDRESS 10 + +#define FIELDS_PER_PAGE 18 + +#define TB_FIELD 1 +#define CB_FIELD 3 +#define BT_FIELD 5 +#define IO_FIELD 7 +#define ID_FIELD 8 +#define ME_FIELD 9 +#define TTY_FIELD 11 +#define CU_FIELD 13 +#define PR_FIELD 15 +#define MPR_FIELD 17 + +#define MAX_FIELD 512 + +#define INIT 0 +#define NITEMS 128 +#define MAX_ITEM 512 + +#define DSCRINST 1 +#define DSCRNUM 3 +#define ALTPINQ 5 +#define SSAVE 7 + +#define DSCR "32" +#define ONETONINE "123456789" +#define ALL "1234567890" + + +struct cnode { + struct cnode *next; + int type; + int numbrd; + + union { + struct { + char type; /* Board Type */ + short port; /* I/O Address */ + char *portstr; /* I/O Address in string */ + long addr; /* Memory Address */ + char *addrstr; /* Memory Address in string */ + long pcibus; /* PCI BUS */ + char *pcibusstr; /* PCI BUS in string */ + long pcislot; /* PCI SLOT */ + char *pcislotstr; /* PCI SLOT in string */ + char nport; /* Number of Ports */ + char *id; /* tty id */ + int start; /* start of tty counting */ + char *method; /* Install method */ + char v_type; + char v_port; + char v_addr; + char v_pcibus; + char v_pcislot; + char v_nport; + char v_id; + char v_start; + char v_method; + char line1; + char line2; + char conc1; /* total concs in line1 */ + char conc2; /* total concs in line2 */ + char module1; /* total modules for line1 */ + char module2; /* total modules for line2 */ + char *status; /* config status */ + char *dimstatus; /* Y/N */ + int status_index; /* field pointer */ + } board; + + struct { + char *cable; + char v_cable; + char speed; + char v_speed; + } line; + + struct { + char type; + char *connect; + char speed; + char nport; + char *id; + char *idstr; + int start; + char v_type; + char v_connect; + char v_speed; + char v_nport; + char v_id; + char v_start; + } conc; + + struct { + char type; + char nport; + char *id; + char *idstr; + int start; + char v_type; + char v_nport; + char v_id; + char v_start; + } module; + + char *ttyname; + + char *cuname; + + char *printname; + + int majornumber; + + int altpin; + + int ttysize; + + int chsize; + + int bssize; + + int unsize; + + int f2size; + + int vpixsize; + + int useintr; + } u; +}; + +#endif diff --git a/drivers/staging/dgap/dgap_downld.h b/drivers/staging/dgap/dgap_downld.h new file mode 100644 index 000000000000..f79e65cd1d51 --- /dev/null +++ b/drivers/staging/dgap/dgap_downld.h @@ -0,0 +1,69 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: dgap_downld.h,v 1.1 2009/10/23 14:01:57 markh Exp $ + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + */ + +/* +** downld.h +** - describes the interface between the user level download process +** and the concentrator download driver. +*/ + +#ifndef _DGAP_DOWNLD_H_ +#define _DGAP_DOWNLD_H_ + + +struct fepimg { + int type; /* board type */ + int len; /* length of image */ + char fepimage[1]; /* begining of image */ +}; + +struct downldio { + unsigned int req_type; /* FEP or concentrator */ + unsigned int bdid; /* opaque board identifier */ + union { + struct downld_t dl; /* download structure */ + struct fepimg fi; /* fep/bios image structure */ + } image; +}; + +#define DIGI_DLREQ_GET (('d'<<8) | 220) +#define DIGI_DLREQ_SET (('d'<<8) | 221) + +#define DIGI_DL_NUKE (('d'<<8) | 222) /* Not really a dl request, but + dangerous enuff to not put in + digi.h */ +/* Packed bits of intarg for DIGI_DL_NUKE */ +#define DIGI_NUKE_RESET_ALL (1 << 31) +#define DIGI_NUKE_INHIBIT_POLLER (1 << 30) +#define DIGI_NUKE_BRD_NUMB 0x0f + + + +#define DLREQ_BIOS 0 +#define DLREQ_FEP 1 +#define DLREQ_CONC 2 +#define DLREQ_CONFIG 3 +#define DLREQ_DEVCREATE 4 + +#endif diff --git a/drivers/staging/dgap/dgap_driver.c b/drivers/staging/dgap/dgap_driver.c new file mode 100644 index 000000000000..b4f54ef8493d --- /dev/null +++ b/drivers/staging/dgap/dgap_driver.c @@ -0,0 +1,1063 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + * $Id: dgap_driver.c,v 1.3 2011/06/21 10:35:16 markh Exp $ + */ + + +#include +#include +#include +#include +#include /* For udelay */ +#include /* For copy_from_user/copy_to_user */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +#include +#endif + +#include "dgap_driver.h" +#include "dgap_pci.h" +#include "dgap_proc.h" +#include "dgap_fep5.h" +#include "dgap_tty.h" +#include "dgap_conf.h" +#include "dgap_parse.h" +#include "dgap_mgmt.h" +#include "dgap_trace.h" +#include "dgap_sysfs.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Digi International, http://www.digi.com"); +MODULE_DESCRIPTION("Driver for the Digi International EPCA PCI based product line"); +MODULE_SUPPORTED_DEVICE("dgap"); + +/* + * insmod command line overrideable parameters + * + * NOTE: we use a set of macros to create the variables, which allows + * us to specify the variable type, name, initial value, and description. + */ +PARM_INT(debug, 0x00, 0644, "Driver debugging level"); +PARM_INT(rawreadok, 1, 0644, "Bypass flip buffers on input"); +PARM_INT(trcbuf_size, 0x100000, 0644, "Debugging trace buffer size."); + + +/************************************************************************** + * + * protos for this file + * + */ + +static int dgap_start(void); +static void dgap_init_globals(void); +static int dgap_found_board(struct pci_dev *pdev, int id); +static void dgap_cleanup_board(struct board_t *brd); +static void dgap_poll_handler(ulong dummy); +static int dgap_init_pci(void); +static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); +static void dgap_remove_one(struct pci_dev *dev); +static int dgap_probe1(struct pci_dev *pdev, int card_type); +static void dgap_mbuf(struct board_t *brd, const char *fmt, ...); +static int dgap_do_remap(struct board_t *brd); +static irqreturn_t dgap_intr(int irq, void *voidbrd); + +/* Driver load/unload functions */ +int dgap_init_module(void); +void dgap_cleanup_module(void); + +module_init(dgap_init_module); +module_exit(dgap_cleanup_module); + + +/* + * File operations permitted on Control/Management major. + */ +static struct file_operations DgapBoardFops = +{ + .owner = THIS_MODULE, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = dgap_mgmt_ioctl, +#else + .ioctl: = dgap_mgmt_ioctl, +#endif + .open = dgap_mgmt_open, + .release = dgap_mgmt_close +}; + + +/* + * Globals + */ +uint dgap_NumBoards; +struct board_t *dgap_Board[MAXBOARDS]; +DEFINE_SPINLOCK(dgap_global_lock); +ulong dgap_poll_counter; +char *dgap_config_buf; +int dgap_driver_state = DRIVER_INITIALIZED; +DEFINE_SPINLOCK(dgap_dl_lock); +wait_queue_head_t dgap_dl_wait; +int dgap_dl_action; +int dgap_poll_tick = 20; /* Poll interval - 20 ms */ + +/* + * Static vars. + */ +static int dgap_Major_Control_Registered = FALSE; +static uint dgap_driver_start = FALSE; + +static struct class * dgap_class; + +/* + * 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; + + +static struct pci_device_id dgap_pci_tbl[] = { + { DIGI_VID, PCI_DEVICE_XEM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { DIGI_VID, PCI_DEVICE_CX_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { DIGI_VID, PCI_DEVICE_CX_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { DIGI_VID, PCI_DEVICE_EPCJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, + { DIGI_VID, PCI_DEVICE_920_2_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { DIGI_VID, PCI_DEVICE_920_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, + { DIGI_VID, PCI_DEVICE_920_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, + { DIGI_VID, PCI_DEVICE_XR_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 }, + { DIGI_VID, PCI_DEVICE_XRJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 }, + { DIGI_VID, PCI_DEVICE_XR_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 }, + { DIGI_VID, PCI_DEVICE_XR_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 }, + { DIGI_VID, PCI_DEVICE_XR_SAIP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 }, + { DIGI_VID, PCI_DEVICE_XR_BULL_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 }, + { DIGI_VID, PCI_DEVICE_920_8_HP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 }, + { DIGI_VID, PCI_DEVICE_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; + uchar *name; + uint maxports; + uint dpatype; +}; + +static struct board_id dgap_Ids[] = +{ + { PPCM, PCI_DEVICE_XEM_NAME, 64, (T_PCXM | T_PCLITE | T_PCIBUS) }, + { PCX, PCI_DEVICE_CX_NAME, 128, (T_CX | T_PCIBUS) }, + { PCX, PCI_DEVICE_CX_IBM_NAME, 128, (T_CX | T_PCIBUS) }, + { PEPC, PCI_DEVICE_EPCJ_NAME, 224, (T_EPC | T_PCIBUS) }, + { APORT2_920P, PCI_DEVICE_920_2_NAME, 2, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { APORT4_920P, PCI_DEVICE_920_4_NAME, 4, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { APORT8_920P, PCI_DEVICE_920_8_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { PAPORT8, PCI_DEVICE_XR_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { PAPORT8, PCI_DEVICE_XRJ_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { PAPORT8, PCI_DEVICE_XR_422_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { PAPORT8, PCI_DEVICE_XR_IBM_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { PAPORT8, PCI_DEVICE_XR_SAIP_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { PAPORT8, PCI_DEVICE_XR_BULL_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { APORT8_920P, PCI_DEVICE_920_8_HP_NAME, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + { PPCM, PCI_DEVICE_XEM_HP_NAME, 64, (T_PCXM | T_PCLITE | T_PCIBUS) }, + {0,} /* 0 terminated list. */ +}; + +static struct pci_driver dgap_driver = { + .name = "dgap", + .probe = dgap_init_one, + .id_table = dgap_pci_tbl, + .remove = dgap_remove_one, +}; + + +char *dgap_state_text[] = { + "Board Failed", + "Configuration for board not found.\n\t\t\tRun mpi to configure board.", + "Board Found", + "Need Reset", + "Finished Reset", + "Need Config", + "Finished Config", + "Need Device Creation", + "Requested Device Creation", + "Finished Device Creation", + "Need BIOS Load", + "Requested BIOS", + "Doing BIOS Load", + "Finished BIOS Load", + "Need FEP Load", + "Requested FEP", + "Doing FEP Load", + "Finished FEP Load", + "Requested PROC creation", + "Finished PROC creation", + "Board READY", +}; + +char *dgap_driver_state_text[] = { + "Driver Initialized", + "Driver needs configuration load.", + "Driver requested configuration from download daemon.", + "Driver Ready." +}; + + + +/************************************************************************ + * + * Driver load/unload functions + * + ************************************************************************/ + +/* + * init_module() + * + * Module load. This is where it all starts. + */ +int dgap_init_module(void) +{ + int rc = 0; + + APR(("%s, Digi International Part Number %s\n", DG_NAME, DG_PART)); + + /* + * Initialize global stuff + */ + rc = dgap_start(); + + if (rc < 0) { + return(rc); + } + + /* + * Find and configure all the cards + */ + rc = dgap_init_pci(); + + /* + * If something went wrong in the scan, bail out of driver. + */ + if (rc < 0) { + /* Only unregister the pci driver if it was actually registered. */ + if (dgap_NumBoards) + pci_unregister_driver(&dgap_driver); + else + printk("WARNING: dgap driver load failed. No DGAP boards found.\n"); + + dgap_cleanup_module(); + } + else { + dgap_create_driver_sysfiles(&dgap_driver); + } + + DPR_INIT(("Finished init_module. Returning %d\n", rc)); + return (rc); +} + + +/* + * Start of driver. + */ +static int dgap_start(void) +{ + int rc = 0; + unsigned long flags; + + if (dgap_driver_start == FALSE) { + + dgap_driver_start = TRUE; + + /* make sure that the globals are init'd before we do anything else */ + dgap_init_globals(); + + dgap_NumBoards = 0; + + APR(("For the tools package or updated drivers please visit http://www.digi.com\n")); + + /* + * Register our base character device into the kernel. + * This allows the download daemon to connect to the downld device + * before any of the boards are init'ed. + */ + if (!dgap_Major_Control_Registered) { + /* + * Register management/dpa devices + */ + rc = register_chrdev(DIGI_DGAP_MAJOR, "dgap", &DgapBoardFops); + if (rc < 0) { + APR(("Can't register dgap driver device (%d)\n", rc)); + return (rc); + } + + dgap_class = class_create(THIS_MODULE, "dgap_mgmt"); + device_create(dgap_class, NULL, + MKDEV(DIGI_DGAP_MAJOR, 0), + NULL, "dgap_mgmt"); + device_create(dgap_class, NULL, + MKDEV(DIGI_DGAP_MAJOR, 1), + NULL, "dgap_downld"); + dgap_Major_Control_Registered = TRUE; + } + + /* + * Register our basic stuff in /proc/dgap + */ + dgap_proc_register_basic_prescan(); + + /* + * Init any global tty stuff. + */ + rc = dgap_tty_preinit(); + + if (rc < 0) { + APR(("tty preinit - not enough memory (%d)\n", rc)); + return(rc); + } + + /* Start the poller */ + DGAP_LOCK(dgap_poll_lock, flags); + init_timer(&dgap_poll_timer); + dgap_poll_timer.function = dgap_poll_handler; + dgap_poll_timer.data = 0; + dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick); + dgap_poll_timer.expires = dgap_poll_time; + DGAP_UNLOCK(dgap_poll_lock, flags); + + add_timer(&dgap_poll_timer); + + dgap_driver_state = DRIVER_NEED_CONFIG_LOAD; + } + + return (rc); +} + + +/* + * Register pci driver, and return how many boards we have. + */ +static int dgap_init_pci(void) +{ + return pci_register_driver(&dgap_driver); +} + + +/* returns count (>= 0), or negative on error */ +static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int rc; + + /* wake up and enable device */ + rc = pci_enable_device(pdev); + + if (rc < 0) { + rc = -EIO; + } else { + rc = dgap_probe1(pdev, ent->driver_data); + if (rc == 0) { + dgap_NumBoards++; + DPR_INIT(("Incrementing numboards to %d\n", dgap_NumBoards)); + } + } + return rc; +} + + +static int dgap_probe1(struct pci_dev *pdev, int card_type) +{ + return dgap_found_board(pdev, card_type); +} + + +static void dgap_remove_one(struct pci_dev *dev) +{ + /* Do Nothing */ +} + + +/* + * dgap_cleanup_module() + * + * Module unload. This is where it all ends. + */ +void dgap_cleanup_module(void) +{ + int i; + ulong lock_flags; + + DGAP_LOCK(dgap_poll_lock, lock_flags); + dgap_poll_stop = 1; + DGAP_UNLOCK(dgap_poll_lock, lock_flags); + + /* Turn off poller right away. */ + del_timer_sync( &dgap_poll_timer); + + dgap_proc_unregister_all(); + + dgap_remove_driver_sysfiles(&dgap_driver); + + + if (dgap_Major_Control_Registered) { + device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0)); + device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 1)); + class_destroy(dgap_class); + unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); + } + + if (dgap_config_buf) + kfree(dgap_config_buf); + + for (i = 0; i < dgap_NumBoards; ++i) { + dgap_remove_ports_sysfiles(dgap_Board[i]); + dgap_tty_uninit(dgap_Board[i]); + dgap_cleanup_board(dgap_Board[i]); + } + + dgap_tty_post_uninit(); + +#if defined(DGAP_TRACER) + /* last thing, make sure we release the tracebuffer */ + dgap_tracer_free(); +#endif + if (dgap_NumBoards) + pci_unregister_driver(&dgap_driver); +} + + +/* + * dgap_cleanup_board() + * + * Free all the memory associated with a board + */ +static void dgap_cleanup_board(struct board_t *brd) +{ + int i = 0; + + if(!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + if (brd->intr_used && brd->irq) + free_irq(brd->irq, brd); + + tasklet_kill(&brd->helper_tasklet); + + if (brd->re_map_port) { + release_mem_region(brd->membase + 0x200000, 0x200000); + iounmap(brd->re_map_port); + brd->re_map_port = NULL; + } + + if (brd->re_map_membase) { + release_mem_region(brd->membase, 0x200000); + iounmap(brd->re_map_membase); + brd->re_map_membase = NULL; + } + + if (brd->msgbuf_head) { + unsigned long flags; + + DGAP_LOCK(dgap_global_lock, flags); + brd->msgbuf = NULL; + printk(brd->msgbuf_head); + kfree(brd->msgbuf_head); + brd->msgbuf_head = NULL; + DGAP_UNLOCK(dgap_global_lock, flags); + } + + /* Free all allocated channels structs */ + for (i = 0; i < MAXPORTS ; i++) { + if (brd->channels[i]) { + kfree(brd->channels[i]); + brd->channels[i] = NULL; + } + } + + if (brd->flipbuf) + kfree(brd->flipbuf); + if (brd->flipflagbuf) + kfree(brd->flipflagbuf); + + dgap_Board[brd->boardnum] = NULL; + + kfree(brd); +} + + +/* + * dgap_found_board() + * + * A board has been found, init it. + */ +static int dgap_found_board(struct pci_dev *pdev, int id) +{ + struct board_t *brd; + unsigned int pci_irq; + int i = 0; + unsigned long flags; + + /* get the board structure and prep it */ + brd = dgap_Board[dgap_NumBoards] = + (struct board_t *) dgap_driver_kzmalloc(sizeof(struct board_t), GFP_KERNEL); + if (!brd) { + APR(("memory allocation for board structure failed\n")); + return(-ENOMEM); + } + + /* make a temporary message buffer for the boot messages */ + brd->msgbuf = brd->msgbuf_head = + (char *) dgap_driver_kzmalloc(sizeof(char) * 8192, GFP_KERNEL); + if(!brd->msgbuf) { + kfree(brd); + APR(("memory allocation for board msgbuf failed\n")); + return(-ENOMEM); + } + + /* store the info for the board we've found */ + brd->magic = DGAP_BOARD_MAGIC; + brd->boardnum = dgap_NumBoards; + brd->firstminor = 0; + 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); + + DGAP_SPINLOCK_INIT(brd->bd_lock); + + brd->state = BOARD_FOUND; + brd->runwait = 0; + 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_DEVICE_XRJ_DID || brd->device == PCI_DEVICE_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) { + APR(("card has no PCI IO resources, failing board.\n")); + return -ENODEV; + } + + if (brd->membase & 1) + brd->membase &= ~3; + else + brd->membase &= ~15; + + /* + * On the PCI boards, there is no IO space allocated + * The I/O registers will be in the first 3 bytes of the + * upper 2MB of the 4MB memory space. The board memory + * will be mapped into the low 2MB of the 4MB memory space + */ + brd->port = brd->membase + PCI_IO_OFFSET; + brd->port_end = brd->port + PCI_IO_SIZE; + + + /* + * Special initialization for non-PLX boards + */ + if (brd->device != PCI_DEVICE_XRJ_DID && brd->device != PCI_DEVICE_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); + + /* Log the information about the board */ + dgap_mbuf(brd, DRVSTR": board %d: %s (rev %d), irq %d\n", + dgap_NumBoards, brd->name, brd->rev, brd->irq); + + DPR_INIT(("dgap_scan(%d) - printing out the msgbuf\n", i)); + DGAP_LOCK(dgap_global_lock, flags); + brd->msgbuf = NULL; + printk(brd->msgbuf_head); + kfree(brd->msgbuf_head); + brd->msgbuf_head = NULL; + DGAP_UNLOCK(dgap_global_lock, flags); + + i = dgap_do_remap(brd); + if (i) + brd->state = BOARD_FAILED; + else + brd->state = NEED_RESET; + + return(0); +} + + +int dgap_finalize_board_init(struct board_t *brd) { + + int rc; + + DPR_INIT(("dgap_finalize_board_init() - start\n")); + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return(-ENODEV); + + DPR_INIT(("dgap_finalize_board_init() - start #2\n")); + + brd->use_interrupts = dgap_config_get_useintr(brd); + + /* + * Set up our interrupt handler if we are set to do interrupts. + */ + if (brd->use_interrupts && brd->irq) { + + rc = request_irq(brd->irq, dgap_intr, IRQF_SHARED, "DGAP", brd); + + if (rc) { + dgap_mbuf(brd, DRVSTR": Failed to hook IRQ %d. Board will work in poll mode.\n", + brd->irq); + brd->intr_used = 0; + } + else + brd->intr_used = 1; + } else { + brd->intr_used = 0; + } + + return(0); +} + + +/* + * Remap PCI memory. + */ +static int dgap_do_remap(struct board_t *brd) +{ + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return -ENXIO; + + if (!request_mem_region(brd->membase, 0x200000, "dgap")) { + APR(("dgap: mem_region %lx already in use.\n", brd->membase)); + return -ENOMEM; + } + + if (!request_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000, "dgap")) { + APR(("dgap: mem_region IO %lx already in use.\n", + brd->membase + PCI_IO_OFFSET)); + release_mem_region(brd->membase, 0x200000); + return -ENOMEM; + } + + brd->re_map_membase = ioremap(brd->membase, 0x200000); + if (!brd->re_map_membase) { + APR(("dgap: ioremap mem %lx cannot be mapped.\n", brd->membase)); + release_mem_region(brd->membase, 0x200000); + release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); + return -ENOMEM; + } + + brd->re_map_port = ioremap((brd->membase + PCI_IO_OFFSET), 0x200000); + if (!brd->re_map_port) { + release_mem_region(brd->membase, 0x200000); + release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000); + iounmap(brd->re_map_membase); + APR(("dgap: ioremap IO mem %lx cannot be mapped.\n", + brd->membase + PCI_IO_OFFSET)); + return -ENOMEM; + } + + DPR_INIT(("remapped io: 0x%p remapped mem: 0x%p\n", + brd->re_map_port, brd->re_map_membase)); + return 0; +} + + +/***************************************************************************** +* +* 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) +{ + int i; + struct board_t *brd; + unsigned long lock_flags; + unsigned long lock_flags2; + ulong new_time; + + dgap_poll_counter++; + + + /* + * If driver needs the config file still, + * keep trying to wake up the downloader to + * send us the file. + */ + if (dgap_driver_state == DRIVER_NEED_CONFIG_LOAD) { + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + goto schedule_poller; + } + /* + * Do not start the board state machine until + * driver tells us its up and running, and has + * everything it needs. + */ + else 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. + */ + DGAP_LOCK(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; + DGAP_UNLOCK(dgap_poll_lock, lock_flags ); + + if (!dgap_poll_stop) + add_timer(&dgap_poll_timer); +} + + + + +/* + * dgap_intr() + * + * Driver interrupt handler. + */ +static irqreturn_t dgap_intr(int irq, void *voidbrd) +{ + struct board_t *brd = (struct board_t *) voidbrd; + + if (!brd) { + APR(("Received interrupt (%d) with null board associated\n", irq)); + return IRQ_NONE; + } + + /* + * Check to make sure its for us. + */ + if (brd->magic != DGAP_BOARD_MAGIC) { + APR(("Received interrupt (%d) with a board pointer that wasn't ours!\n", irq)); + return IRQ_NONE; + } + + brd->intr_count++; + + /* + * Schedule tasklet to run at a better time. + */ + tasklet_schedule(&brd->helper_tasklet); + return IRQ_HANDLED; +} + + +/* + * dgap_init_globals() + * + * This is where we initialize the globals from the static insmod + * configuration variables. These are declared near the head of + * this file. + */ +static void dgap_init_globals(void) +{ + int i = 0; + + dgap_rawreadok = rawreadok; + dgap_trcbuf_size = trcbuf_size; + dgap_debug = debug; + + for (i = 0; i < MAXBOARDS; i++) { + dgap_Board[i] = NULL; + } + + init_timer( &dgap_poll_timer ); + + init_waitqueue_head(&dgap_dl_wait); + dgap_dl_action = 0; +} + + +/************************************************************************ + * + * Utility functions + * + ************************************************************************/ + + +/* + * dgap_driver_kzmalloc() + * + * Malloc and clear memory, + */ +void *dgap_driver_kzmalloc(size_t size, int priority) +{ + void *p = kmalloc(size, priority); + if(p) + memset(p, 0, size); + return(p); +} + + +/* + * dgap_mbuf() + * + * Used to print to the message buffer during board init. + */ +static void dgap_mbuf(struct board_t *brd, const char *fmt, ...) { + va_list ap; + char buf[1024]; + int i; + unsigned long flags; + + DGAP_LOCK(dgap_global_lock, flags); + + /* Format buf using fmt and arguments contained in ap. */ + va_start(ap, fmt); + i = vsprintf(buf, fmt, ap); + va_end(ap); + + DPR((buf)); + + if (!brd || !brd->msgbuf) { + printk(buf); + DGAP_UNLOCK(dgap_global_lock, flags); + return; + } + + memcpy(brd->msgbuf, buf, strlen(buf)); + brd->msgbuf += strlen(buf); + *brd->msgbuf = 0; + + DGAP_UNLOCK(dgap_global_lock, flags); +} + + +/* + * dgap_ms_sleep() + * + * Put the driver to sleep for x ms's + * + * Returns 0 if timed out, !0 (showing signal) if interrupted by a signal. + */ +int dgap_ms_sleep(ulong ms) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((ms * HZ) / 1000); + return (signal_pending(current)); +} + + + +/* + * dgap_ioctl_name() : Returns a text version of each ioctl value. + */ +char *dgap_ioctl_name(int cmd) +{ + switch(cmd) { + + case TCGETA: return("TCGETA"); + case TCGETS: return("TCGETS"); + case TCSETA: return("TCSETA"); + case TCSETS: return("TCSETS"); + case TCSETAW: return("TCSETAW"); + case TCSETSW: return("TCSETSW"); + case TCSETAF: return("TCSETAF"); + case TCSETSF: return("TCSETSF"); + case TCSBRK: return("TCSBRK"); + case TCXONC: return("TCXONC"); + case TCFLSH: return("TCFLSH"); + case TIOCGSID: return("TIOCGSID"); + + case TIOCGETD: return("TIOCGETD"); + case TIOCSETD: return("TIOCSETD"); + case TIOCGWINSZ: return("TIOCGWINSZ"); + case TIOCSWINSZ: return("TIOCSWINSZ"); + + case TIOCMGET: return("TIOCMGET"); + case TIOCMSET: return("TIOCMSET"); + case TIOCMBIS: return("TIOCMBIS"); + case TIOCMBIC: return("TIOCMBIC"); + + /* from digi.h */ + case DIGI_SETA: return("DIGI_SETA"); + case DIGI_SETAW: return("DIGI_SETAW"); + case DIGI_SETAF: return("DIGI_SETAF"); + case DIGI_SETFLOW: return("DIGI_SETFLOW"); + case DIGI_SETAFLOW: return("DIGI_SETAFLOW"); + case DIGI_GETFLOW: return("DIGI_GETFLOW"); + case DIGI_GETAFLOW: return("DIGI_GETAFLOW"); + case DIGI_GETA: return("DIGI_GETA"); + case DIGI_GEDELAY: return("DIGI_GEDELAY"); + case DIGI_SEDELAY: return("DIGI_SEDELAY"); + case DIGI_GETCUSTOMBAUD: return("DIGI_GETCUSTOMBAUD"); + case DIGI_SETCUSTOMBAUD: return("DIGI_SETCUSTOMBAUD"); + case TIOCMODG: return("TIOCMODG"); + case TIOCMODS: return("TIOCMODS"); + case TIOCSDTR: return("TIOCSDTR"); + case TIOCCDTR: return("TIOCCDTR"); + + default: return("unknown"); + } +} diff --git a/drivers/staging/dgap/dgap_driver.h b/drivers/staging/dgap/dgap_driver.h new file mode 100644 index 000000000000..b76ec3c39ef0 --- /dev/null +++ b/drivers/staging/dgap/dgap_driver.h @@ -0,0 +1,626 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + ************************************************************************* + * + * Driver includes + * + *************************************************************************/ + +#ifndef __DGAP_DRIVER_H +#define __DGAP_DRIVER_H + +#include /* To get the current Linux version */ +#include /* To pick up the varions Linux types */ +#include /* To pick up the various tty structs/defines */ +#include /* For irqreturn_t type */ + +#include "dgap_types.h" /* Additional types needed by the Digi header files */ +#include "digi.h" /* Digi specific ioctl header */ +#include "dgap_kcompat.h" /* Kernel 2.4/2.6 compat includes */ +#include "dgap_sysfs.h" /* Support for SYSFS */ + +/************************************************************************* + * + * Driver defines + * + *************************************************************************/ + +/* + * Driver identification, error and debugging statments + * + * In theory, you can change all occurances of "digi" in the next + * three lines, and the driver printk's will all automagically change. + * + * APR((fmt, args, ...)); Always prints message + * DPR((fmt, args, ...)); Only prints if DGAP_TRACER is defined at + * compile time and dgap_debug!=0 + */ +#define PROCSTR "dgap" /* /proc entries */ +#define DEVSTR "/dev/dg/dgap" /* /dev entries */ +#define DRVSTR "dgap" /* Driver name string + * displayed by APR */ +#define APR(args) do { PRINTF_TO_KMEM(args); printk(DRVSTR": "); printk args; \ + } while (0) +#define RAPR(args) do { PRINTF_TO_KMEM(args); printk args; } while (0) + +#define TRC_TO_CONSOLE 1 + +/* + * Debugging levels can be set using debug insmod variable + * They can also be compiled out completely. + */ + +#define DBG_INIT (dgap_debug & 0x01) +#define DBG_BASIC (dgap_debug & 0x02) +#define DBG_CORE (dgap_debug & 0x04) + +#define DBG_OPEN (dgap_debug & 0x08) +#define DBG_CLOSE (dgap_debug & 0x10) +#define DBG_READ (dgap_debug & 0x20) +#define DBG_WRITE (dgap_debug & 0x40) + +#define DBG_IOCTL (dgap_debug & 0x80) + +#define DBG_PROC (dgap_debug & 0x100) +#define DBG_PARAM (dgap_debug & 0x200) +#define DBG_PSCAN (dgap_debug & 0x400) +#define DBG_EVENT (dgap_debug & 0x800) + +#define DBG_DRAIN (dgap_debug & 0x1000) +#define DBG_CARR (dgap_debug & 0x2000) + +#define DBG_MGMT (dgap_debug & 0x4000) + + +#if defined(DGAP_TRACER) + +# if defined(TRC_TO_KMEM) +/* Choose one: */ +# define TRC_ON_OVERFLOW_WRAP_AROUND +# undef TRC_ON_OVERFLOW_SHIFT_BUFFER +# endif //TRC_TO_KMEM + +# define TRC_MAXMSG 1024 +# define TRC_OVERFLOW "(OVERFLOW)" +# define TRC_DTRC "/usr/bin/dtrc" + +#if defined TRC_TO_CONSOLE +#define PRINTF_TO_CONSOLE(args) { printk(DRVSTR": "); printk args; } +#else //!defined TRACE_TO_CONSOLE +#define PRINTF_TO_CONSOLE(args) +#endif + +#if defined TRC_TO_KMEM +#define PRINTF_TO_KMEM(args) dgap_tracef args +#else //!defined TRC_TO_KMEM +#define PRINTF_TO_KMEM(args) +#endif + +#define TRC(args) { PRINTF_TO_KMEM(args); PRINTF_TO_CONSOLE(args) } + +# define DPR_INIT(ARGS) if (DBG_INIT) TRC(ARGS) +# define DPR_BASIC(ARGS) if (DBG_BASIC) TRC(ARGS) +# define DPR_CORE(ARGS) if (DBG_CORE) TRC(ARGS) +# define DPR_OPEN(ARGS) if (DBG_OPEN) TRC(ARGS) +# define DPR_CLOSE(ARGS) if (DBG_CLOSE) TRC(ARGS) +# define DPR_READ(ARGS) if (DBG_READ) TRC(ARGS) +# define DPR_WRITE(ARGS) if (DBG_WRITE) TRC(ARGS) +# define DPR_IOCTL(ARGS) if (DBG_IOCTL) TRC(ARGS) +# define DPR_PROC(ARGS) if (DBG_PROC) TRC(ARGS) +# define DPR_PARAM(ARGS) if (DBG_PARAM) TRC(ARGS) +# define DPR_PSCAN(ARGS) if (DBG_PSCAN) TRC(ARGS) +# define DPR_EVENT(ARGS) if (DBG_EVENT) TRC(ARGS) +# define DPR_DRAIN(ARGS) if (DBG_DRAIN) TRC(ARGS) +# define DPR_CARR(ARGS) if (DBG_CARR) TRC(ARGS) +# define DPR_MGMT(ARGS) if (DBG_MGMT) TRC(ARGS) + +# define DPR(ARGS) if (dgap_debug) TRC(ARGS) +# define P(X) dgap_tracef(#X "=%p\n", X) +# define X(X) dgap_tracef(#X "=%x\n", X) + +#else//!defined DGAP_TRACER + +#define PRINTF_TO_KMEM(args) +# define TRC(ARGS) +# define DPR_INIT(ARGS) +# define DPR_BASIC(ARGS) +# define DPR_CORE(ARGS) +# define DPR_OPEN(ARGS) +# define DPR_CLOSE(ARGS) +# define DPR_READ(ARGS) +# define DPR_WRITE(ARGS) +# define DPR_IOCTL(ARGS) +# define DPR_PROC(ARGS) +# define DPR_PARAM(ARGS) +# define DPR_PSCAN(ARGS) +# define DPR_EVENT(ARGS) +# define DPR_DRAIN(ARGS) +# define DPR_CARR(ARGS) +# define DPR_MGMT(ARGS) + +# define DPR(args) + +#endif//DGAP_TRACER + +/* Number of boards we support at once. */ +#define MAXBOARDS 32 +#define MAXPORTS 224 +#define MAXTTYNAMELEN 200 + +/* Our 3 magic numbers for our board, channel and unit structs */ +#define DGAP_BOARD_MAGIC 0x5c6df104 +#define DGAP_CHANNEL_MAGIC 0x6c6df104 +#define DGAP_UNIT_MAGIC 0x7c6df104 + +/* Serial port types */ +#define DGAP_SERIAL 0 +#define DGAP_PRINT 1 + +#define SERIAL_TYPE_NORMAL 1 + +/* 4 extra for alignment play space */ +#define WRITEBUFLEN ((4096) + 4) +#define MYFLIPLEN N_TTY_BUF_SIZE + +#define SBREAK_TIME 0x25 +#define U2BSIZE 0x400 + +#define dgap_jiffies_from_ms(a) (((a) * HZ) / 1000) + +/* + * Our major for the mgmt devices. + * + * We can use 22, because Digi was allocated 22 and 23 for the epca driver. + * 22 has now become obsolete now that the "cu" devices have + * been removed from 2.6. + * Also, this *IS* the epca driver, just PCI only now. + */ +#ifndef DIGI_DGAP_MAJOR +# define DIGI_DGAP_MAJOR 22 +#endif + +/* + * The parameters we use to define the periods of the moving averages. + */ +#define MA_PERIOD (HZ / 10) +#define SMA_DUR (1 * HZ) +#define EMA_DUR (1 * HZ) +#define SMA_NPERIODS (SMA_DUR / MA_PERIOD) +#define EMA_NPERIODS (EMA_DUR / MA_PERIOD) + +/* + * Define a local default termios struct. All ports will be created + * with this termios initially. This is the same structure that is defined + * as the default in tty_io.c with the same settings overriden as in serial.c + * + * In short, this should match the internal serial ports' defaults. + */ +#define DEFAULT_IFLAGS (ICRNL | IXON) +#define DEFAULT_OFLAGS (OPOST | ONLCR) +#define DEFAULT_CFLAGS (B9600 | CS8 | CREAD | HUPCL | CLOCAL) +#define DEFAULT_LFLAGS (ISIG | ICANON | ECHO | ECHOE | ECHOK | \ + ECHOCTL | ECHOKE | IEXTEN) + +#ifndef _POSIX_VDISABLE +#define _POSIX_VDISABLE '\0' +#endif + +#define SNIFF_MAX 65536 /* Sniff buffer size (2^n) */ +#define SNIFF_MASK (SNIFF_MAX - 1) /* Sniff wrap mask */ + +#define VPDSIZE (512) + +/* + * Lock function/defines. + * Makes spotting lock/unlock locations easier. + */ +# define DGAP_SPINLOCK_INIT(x) spin_lock_init(&(x)) +# define DGAP_LOCK(x,y) spin_lock_irqsave(&(x), y) +# define DGAP_UNLOCK(x,y) spin_unlock_irqrestore(&(x), y) +# define DGAP_TRYLOCK(x,y) spin_trylock(&(x)) + +/* + * All the possible states the driver can be while being loaded. + */ +enum { + DRIVER_INITIALIZED = 0, + DRIVER_NEED_CONFIG_LOAD, + DRIVER_REQUESTED_CONFIG, + DRIVER_READY +}; + +/* + * All the possible states the board can be while booting up. + */ +enum { + BOARD_FAILED = 0, + CONFIG_NOT_FOUND, + BOARD_FOUND, + NEED_RESET, + FINISHED_RESET, + NEED_CONFIG, + FINISHED_CONFIG, + NEED_DEVICE_CREATION, + REQUESTED_DEVICE_CREATION, + FINISHED_DEVICE_CREATION, + NEED_BIOS_LOAD, + REQUESTED_BIOS, + WAIT_BIOS_LOAD, + FINISHED_BIOS_LOAD, + NEED_FEP_LOAD, + REQUESTED_FEP, + WAIT_FEP_LOAD, + FINISHED_FEP_LOAD, + NEED_PROC_CREATION, + FINISHED_PROC_CREATION, + BOARD_READY +}; + +/* + * All the possible states that a requested concentrator image can be in. + */ +enum { + NO_PENDING_CONCENTRATOR_REQUESTS = 0, + NEED_CONCENTRATOR, + REQUESTED_CONCENTRATOR +}; + +extern char *dgap_state_text[]; +extern char *dgap_driver_state_text[]; + + +/* + * Modem line constants are defined as macros because DSR and + * DCD are swapable using the ditty altpin option. + */ +#define D_CD(ch) ch->ch_cd /* Carrier detect */ +#define D_DSR(ch) ch->ch_dsr /* Data set ready */ +#define D_RTS(ch) DM_RTS /* Request to send */ +#define D_CTS(ch) DM_CTS /* Clear to send */ +#define D_RI(ch) DM_RI /* Ring indicator */ +#define D_DTR(ch) DM_DTR /* Data terminal ready */ + + +/************************************************************************* + * + * Structures and closely related defines. + * + *************************************************************************/ + + +/* + * A structure to hold a statistics counter. We also + * compute moving averages for this counter. + */ +struct macounter +{ + u32 cnt; /* Total count */ + ulong accum; /* Acuumulator per period */ + ulong sma; /* Simple moving average */ + ulong ema; /* Exponential moving average */ +}; + + +/************************************************************************ + * Device flag definitions for bd_flags. + ************************************************************************/ +#define BD_FEP5PLUS 0x0001 /* Supports FEP5 Plus commands */ +#define BD_HAS_VPD 0x0002 /* Board has VPD info available */ + + +/* + * Per-board information + */ +struct board_t +{ + int magic; /* Board Magic number. */ + int boardnum; /* Board number: 0-3 */ + int firstminor; /* First minor, e.g. 0, 30, 60 */ + + int type; /* Type of board */ + char *name; /* Product Name */ + struct pci_dev *pdev; /* Pointer to the pci_dev struct */ + u16 vendor; /* PCI vendor ID */ + u16 device; /* PCI device ID */ + u16 subvendor; /* PCI subsystem vendor ID */ + u16 subdevice; /* PCI subsystem device ID */ + uchar rev; /* PCI revision ID */ + uint pci_bus; /* PCI bus value */ + uint pci_slot; /* PCI slot value */ + u16 maxports; /* MAX ports this board can handle */ + uchar vpd[VPDSIZE]; /* VPD of board, if found */ + u32 bd_flags; /* Board flags */ + + spinlock_t bd_lock; /* Used to protect board */ + + u32 state; /* State of card. */ + wait_queue_head_t state_wait; /* Place to sleep on for state change */ + + struct tasklet_struct helper_tasklet; /* Poll helper tasklet */ + + u32 wait_for_bios; + u32 wait_for_fep; + + struct cnode * bd_config; /* Config of board */ + + u16 nasync; /* Number of ports on card */ + + u32 use_interrupts; /* Should we be interrupt driven? */ + ulong irq; /* Interrupt request number */ + ulong intr_count; /* Count of interrupts */ + u32 intr_used; /* Non-zero if using interrupts */ + u32 intr_running; /* Non-zero if FEP knows its doing interrupts */ + + ulong port; /* Start of base io port of the card */ + ulong port_end; /* End of base io port of the card */ + ulong membase; /* Start of base memory of the card */ + ulong membase_end; /* End of base memory of the card */ + + uchar *re_map_port; /* Remapped io port of the card */ + uchar *re_map_membase;/* Remapped memory of the card */ + + uchar runwait; /* # Processes waiting for FEP */ + uchar inhibit_poller; /* Tells the poller to leave us alone */ + + struct channel_t *channels[MAXPORTS]; /* array of pointers to our channels. */ + + struct tty_driver *SerialDriver; + char SerialName[200]; + struct tty_driver *PrintDriver; + char PrintName[200]; + + u32 dgap_Major_Serial_Registered; + u32 dgap_Major_TransparentPrint_Registered; + + u32 dgap_Serial_Major; + u32 dgap_TransparentPrint_Major; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + u32 TtyRefCnt; +#endif + + struct bs_t *bd_bs; /* Base structure pointer */ + + char *flipbuf; /* Our flip buffer, alloced if board is found */ + char *flipflagbuf; /* Our flip flag buffer, alloced if board is found */ + + u16 dpatype; /* The board "type", as defined by DPA */ + u16 dpastatus; /* The board "status", as defined by DPA */ + wait_queue_head_t kme_wait; /* Needed for DPA support */ + + u32 conc_dl_status; /* Status of any pending conc download */ + /* + * Mgmt data. + */ + char *msgbuf_head; + char *msgbuf; + + /* /proc/ entries */ + struct proc_dir_entry *proc_entry_pointer; + struct dgap_proc_entry *dgap_board_table; +}; + + + +/************************************************************************ + * Unit flag definitions for un_flags. + ************************************************************************/ +#define UN_ISOPEN 0x0001 /* Device is open */ +#define UN_CLOSING 0x0002 /* Line is being closed */ +#define UN_IMM 0x0004 /* Service immediately */ +#define UN_BUSY 0x0008 /* Some work this channel */ +#define UN_BREAKI 0x0010 /* Input break received */ +#define UN_PWAIT 0x0020 /* Printer waiting for terminal */ +#define UN_TIME 0x0040 /* Waiting on time */ +#define UN_EMPTY 0x0080 /* Waiting output queue empty */ +#define UN_LOW 0x0100 /* Waiting output low water mark*/ +#define UN_EXCL_OPEN 0x0200 /* Open for exclusive use */ +#define UN_WOPEN 0x0400 /* Device waiting for open */ +#define UN_WIOCTL 0x0800 /* Device waiting for open */ +#define UN_HANGUP 0x8000 /* Carrier lost */ + +struct device; + +/************************************************************************ + * Structure for terminal or printer unit. + ************************************************************************/ +struct un_t { + int magic; /* Unit Magic Number. */ + struct channel_t *un_ch; + u32 un_time; + u32 un_type; + u32 un_open_count; /* Counter of opens to port */ + struct tty_struct *un_tty;/* Pointer to unit tty structure */ + u32 un_flags; /* Unit flags */ + wait_queue_head_t un_flags_wait; /* Place to sleep to wait on unit */ + u32 un_dev; /* Minor device number */ + tcflag_t un_oflag; /* oflags being done on board */ + tcflag_t un_lflag; /* lflags being done on board */ + struct device *un_sysfs; +}; + + +/************************************************************************ + * Device flag definitions for ch_flags. + ************************************************************************/ +#define CH_PRON 0x0001 /* Printer on string */ +#define CH_OUT 0x0002 /* Dial-out device open */ +#define CH_STOP 0x0004 /* Output is stopped */ +#define CH_STOPI 0x0008 /* Input is stopped */ +#define CH_CD 0x0010 /* Carrier is present */ +#define CH_FCAR 0x0020 /* Carrier forced on */ + +#define CH_RXBLOCK 0x0080 /* Enable rx blocked flag */ +#define CH_WLOW 0x0100 /* Term waiting low event */ +#define CH_WEMPTY 0x0200 /* Term waiting empty event */ +#define CH_RENABLE 0x0400 /* Buffer just emptied */ +#define CH_RACTIVE 0x0800 /* Process active in xxread() */ +#define CH_RWAIT 0x1000 /* Process waiting in xxread() */ +#define CH_BAUD0 0x2000 /* Used for checking B0 transitions */ +#define CH_HANGUP 0x8000 /* Hangup received */ + +/* + * Definitions for ch_sniff_flags + */ +#define SNIFF_OPEN 0x1 +#define SNIFF_WAIT_DATA 0x2 +#define SNIFF_WAIT_SPACE 0x4 + + +/************************************************************************ + * Channel information structure. + ************************************************************************/ +struct channel_t { + int magic; /* Channel Magic Number */ + struct bs_t *ch_bs; /* Base structure pointer */ + struct cm_t *ch_cm; /* Command queue pointer */ + struct board_t *ch_bd; /* Board structure pointer */ + unsigned char *ch_vaddr; /* FEP memory origin */ + unsigned char *ch_taddr; /* Write buffer origin */ + unsigned char *ch_raddr; /* Read buffer origin */ + struct digi_t ch_digi; /* Transparent Print structure */ + struct un_t ch_tun; /* Terminal unit info */ + struct un_t ch_pun; /* Printer unit info */ + + spinlock_t ch_lock; /* provide for serialization */ + wait_queue_head_t ch_flags_wait; + + u32 pscan_state; + uchar pscan_savechar; + + u32 ch_portnum; /* Port number, 0 offset. */ + u32 ch_open_count; /* open count */ + u32 ch_flags; /* Channel flags */ + + + u32 ch_close_delay; /* How long we should drop RTS/DTR for */ + + u32 ch_cpstime; /* Time for CPS calculations */ + + tcflag_t ch_c_iflag; /* channel iflags */ + tcflag_t ch_c_cflag; /* channel cflags */ + tcflag_t ch_c_oflag; /* channel oflags */ + tcflag_t ch_c_lflag; /* channel lflags */ + + u16 ch_fepiflag; /* FEP tty iflags */ + u16 ch_fepcflag; /* FEP tty cflags */ + u16 ch_fepoflag; /* FEP tty oflags */ + u16 ch_wopen; /* Waiting for open process cnt */ + u16 ch_tstart; /* Transmit buffer start */ + u16 ch_tsize; /* Transmit buffer size */ + u16 ch_rstart; /* Receive buffer start */ + u16 ch_rsize; /* Receive buffer size */ + u16 ch_rdelay; /* Receive delay time */ + + u16 ch_tlw; /* Our currently set low water mark */ + + u16 ch_cook; /* Output character mask */ + + uchar ch_card; /* Card channel is on */ + uchar ch_stopc; /* Stop character */ + uchar ch_startc; /* Start character */ + + uchar ch_mostat; /* FEP output modem status */ + uchar ch_mistat; /* FEP input modem status */ + uchar ch_mforce; /* Modem values to be forced */ + uchar ch_mval; /* Force values */ + uchar ch_fepstopc; /* FEP stop character */ + uchar ch_fepstartc; /* FEP start character */ + + uchar ch_astopc; /* Auxiliary Stop character */ + uchar ch_astartc; /* Auxiliary Start character */ + uchar ch_fepastopc; /* Auxiliary FEP stop char */ + uchar ch_fepastartc; /* Auxiliary FEP start char */ + + uchar ch_hflow; /* FEP hardware handshake */ + uchar ch_dsr; /* stores real dsr value */ + uchar ch_cd; /* stores real cd value */ + uchar ch_tx_win; /* channel tx buffer window */ + uchar ch_rx_win; /* channel rx buffer window */ + uint ch_custom_speed; /* Custom baud, if set */ + uint ch_baud_info; /* Current baud info for /proc output */ + ulong ch_rxcount; /* total of data received so far */ + ulong ch_txcount; /* total of data transmitted so far */ + ulong ch_err_parity; /* Count of parity errors on channel */ + ulong ch_err_frame; /* Count of framing errors on channel */ + ulong ch_err_break; /* Count of breaks on channel */ + ulong ch_err_overrun; /* Count of overruns on channel */ + + /* /proc// entries */ + struct proc_dir_entry *proc_entry_pointer; + struct dgap_proc_entry *dgap_channel_table; + + uint ch_sniff_in; + uint ch_sniff_out; + char *ch_sniff_buf; /* Sniff buffer for proc */ + ulong ch_sniff_flags; /* Channel flags */ + wait_queue_head_t ch_sniff_wait; +}; + + +/************************************************************************* + * + * Prototypes for non-static functions used in more than one module + * + *************************************************************************/ + +extern int dgap_ms_sleep(ulong ms); +extern void *dgap_driver_kzmalloc(size_t size, int priority); +extern char *dgap_ioctl_name(int cmd); +extern void dgap_do_bios_load(struct board_t *brd, uchar __user *ubios, int len); +extern void dgap_do_fep_load(struct board_t *brd, uchar __user *ufep, int len); +extern void dgap_do_conc_load(struct board_t *brd, uchar *uaddr, int len); +extern void dgap_do_config_load(uchar __user *uaddr, int len); +extern int dgap_after_config_loaded(void); +extern int dgap_finalize_board_init(struct board_t *brd); + +/* + * Our Global Variables. + */ +extern int dgap_driver_state; /* The state of the driver */ +extern int dgap_debug; /* Debug variable */ +extern int dgap_rawreadok; /* Set if user wants rawreads */ +extern int dgap_poll_tick; /* Poll interval - 20 ms */ +extern spinlock_t dgap_global_lock; /* Driver global spinlock */ +extern uint dgap_NumBoards; /* Total number of boards */ +extern struct board_t *dgap_Board[MAXBOARDS]; /* Array of board structs */ +extern ulong dgap_poll_counter; /* Times the poller has run */ +extern char *dgap_config_buf; /* The config file buffer */ +extern spinlock_t dgap_dl_lock; /* Downloader spinlock */ +extern wait_queue_head_t dgap_dl_wait; /* Wait queue for downloader */ +extern int dgap_dl_action; /* Action flag for downloader */ +extern int dgap_registerttyswithsysfs; /* Should we register the */ + /* ttys with sysfs or not */ + +/* + * Global functions declared in dgap_fep5.c, but must be hidden from + * user space programs. + */ +extern void dgap_poll_tasklet(unsigned long data); +extern void dgap_cmdb(struct channel_t *ch, uchar cmd, uchar byte1, uchar byte2, uint ncmds); +extern void dgap_cmdw(struct channel_t *ch, uchar cmd, u16 word, uint ncmds); +extern void dgap_wmove(struct channel_t *ch, char *buf, uint cnt); +extern int dgap_param(struct tty_struct *tty); +extern void dgap_parity_scan(struct channel_t *ch, unsigned char *cbuf, unsigned char *fbuf, int *len); +extern uint dgap_get_custom_baud(struct channel_t *ch); +extern void dgap_firmware_reset_port(struct channel_t *ch); + +#endif diff --git a/drivers/staging/dgap/dgap_fep5.c b/drivers/staging/dgap/dgap_fep5.c new file mode 100644 index 000000000000..ed3cb151d6da --- /dev/null +++ b/drivers/staging/dgap/dgap_fep5.c @@ -0,0 +1,1957 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + * $Id: dgap_fep5.c,v 1.2 2011/06/21 10:35:40 markh Exp $ + */ + + +#include +#include +#include +#include +#include /* For udelay */ +#include /* For copy_from_user/copy_to_user */ +#include +#include /* For tty_schedule_flip */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +#include +#endif + +#include "dgap_driver.h" +#include "dgap_pci.h" +#include "dgap_proc.h" +#include "dgap_fep5.h" +#include "dgap_tty.h" +#include "dgap_conf.h" +#include "dgap_parse.h" +#include "dgap_mgmt.h" +#include "dgap_trace.h" + +/* + * Our function prototypes + */ +static void dgap_cmdw_ext(struct channel_t *ch, u16 cmd, u16 word, uint ncmds); +static int dgap_event(struct board_t *bd); + +/* + * internal variables + */ +static uint dgap_count = 500; + + +/* + * Loads the dgap.conf config file from the user. + */ +void dgap_do_config_load(uchar __user *uaddr, int len) +{ + int orig_len = len; + char *to_addr; + uchar __user *from_addr = uaddr; + char buf[U2BSIZE]; + int n; + + to_addr = dgap_config_buf = dgap_driver_kzmalloc(len + 1, GFP_ATOMIC); + if (!dgap_config_buf) { + DPR_INIT(("dgap_do_config_load - unable to allocate memory for file\n")); + dgap_driver_state = DRIVER_NEED_CONFIG_LOAD; + return; + } + + n = U2BSIZE; + while (len) { + + if (n > len) + n = len; + + if (copy_from_user((char *) &buf, from_addr, n) == -1 ) + return; + + /* Copy data from buffer to kernel memory */ + memcpy(to_addr, buf, n); + + /* increment counts */ + len -= n; + to_addr += n; + from_addr += n; + n = U2BSIZE; + } + + dgap_config_buf[orig_len] = '\0'; + + to_addr = dgap_config_buf; + dgap_parsefile(&to_addr, TRUE); + + DPR_INIT(("dgap_config_load() finish\n")); + + return; +} + + +int dgap_after_config_loaded(void) +{ + int i = 0; + int rc = 0; + + /* + * Register our ttys, now that we have the config loaded. + */ + for (i = 0; i < dgap_NumBoards; ++i) { + + /* + * Initialize KME waitqueues... + */ + init_waitqueue_head(&(dgap_Board[i]->kme_wait)); + + /* + * allocate flip buffer for board. + */ + dgap_Board[i]->flipbuf = dgap_driver_kzmalloc(MYFLIPLEN, GFP_ATOMIC); + dgap_Board[i]->flipflagbuf = dgap_driver_kzmalloc(MYFLIPLEN, GFP_ATOMIC); + + dgap_proc_register_basic_postscan(i); + } + + return (rc); +} + + + +/*======================================================================= + * + * usertoboard - copy from user space to board space. + * + *=======================================================================*/ +static int dgap_usertoboard(struct board_t *brd, char *to_addr, char __user *from_addr, int len) +{ + char buf[U2BSIZE]; + int n = U2BSIZE; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return(-EFAULT); + + while (len) { + if (n > len) + n = len; + + if (copy_from_user((char *) &buf, from_addr, n) == -1 ) { + return(-EFAULT); + } + + /* Copy data from buffer to card memory */ + memcpy_toio(to_addr, buf, n); + + /* increment counts */ + len -= n; + to_addr += n; + from_addr += n; + n = U2BSIZE; + } + return(0); +} + + +/* + * Copies the BIOS code from the user to the board, + * and starts the BIOS running. + */ +void dgap_do_bios_load(struct board_t *brd, uchar __user *ubios, int len) +{ + uchar *addr; + uint offset; + int i; + + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return; + + DPR_INIT(("dgap_do_bios_load() start\n")); + + addr = brd->re_map_membase; + + /* + * clear POST area + */ + for (i = 0; i < 16; i++) + writeb(0, addr + POSTAREA + i); + + /* + * Download bios + */ + offset = 0x1000; + if (dgap_usertoboard(brd, addr + offset, ubios, len) == -1 ) { + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return; + } + + writel(0x0bf00401, addr); + writel(0, (addr + 4)); + + /* Clear the reset, and change states. */ + writeb(FEPCLR, brd->re_map_port); + brd->state = WAIT_BIOS_LOAD; +} + + +/* + * Checks to see if the BIOS completed running on the card. + */ +static void dgap_do_wait_for_bios(struct board_t *brd) +{ + uchar *addr; + u16 word; + + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return; + + addr = brd->re_map_membase; + word = readw(addr + POSTAREA); + + /* Check to see if BIOS thinks board is good. (GD). */ + if (word == *(u16 *) "GD") { + DPR_INIT(("GOT GD in memory, moving states.\n")); + brd->state = FINISHED_BIOS_LOAD; + return; + } + + /* Give up on board after too long of time taken */ + if (brd->wait_for_bios++ > 5000) { + u16 err1 = readw(addr + SEQUENCE); + u16 err2 = readw(addr + ERROR); + APR(("***WARNING*** %s failed diagnostics. Error #(%x,%x).\n", + brd->name, err1, err2)); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + } +} + + +/* + * Copies the FEP code from the user to the board, + * and starts the FEP running. + */ +void dgap_do_fep_load(struct board_t *brd, uchar __user *ufep, int len) +{ + uchar *addr; + uint offset; + + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return; + + addr = brd->re_map_membase; + + DPR_INIT(("dgap_do_fep_load() for board %s : start\n", brd->name)); + + /* + * Download FEP + */ + offset = 0x1000; + if (dgap_usertoboard(brd, addr + offset, ufep, len) == -1 ) { + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return; + } + + /* + * 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)) { + uchar string[100]; + uchar *config, *xconfig; + 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)); + + /* change states. */ + brd->state = WAIT_FEP_LOAD; + + DPR_INIT(("dgap_do_fep_load() for board %s : finish\n", brd->name)); + +} + + +/* + * Waits for the FEP to report thats its ready for us to use. + */ +static void dgap_do_wait_for_fep(struct board_t *brd) +{ + uchar *addr; + u16 word; + + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase) + return; + + addr = brd->re_map_membase; + + DPR_INIT(("dgap_do_wait_for_fep() for board %s : start. addr: %p\n", brd->name, addr)); + + word = readw(addr + FEPSTAT); + + /* Check to see if FEP is up and running now. */ + if (word == *(u16 *) "OS") { + DPR_INIT(("GOT OS in memory for board %s, moving states.\n", brd->name)); + brd->state = FINISHED_FEP_LOAD; + + /* + * Check to see if the board can support FEP5+ commands. + */ + word = readw(addr + FEP5_PLUS); + if (word == *(u16 *) "5A") { + DPR_INIT(("GOT 5A in memory for board %s, board supports extended FEP5 commands.\n", brd->name)); + brd->bd_flags |= BD_FEP5PLUS; + } + + return; + } + + /* Give up on board after too long of time taken */ + if (brd->wait_for_fep++ > 5000) { + u16 err1 = readw(addr + SEQUENCE); + u16 err2 = readw(addr + ERROR); + APR(("***WARNING*** FEPOS for %s not functioning. Error #(%x,%x).\n", + brd->name, err1, err2)); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + } + + DPR_INIT(("dgap_do_wait_for_fep() for board %s : finish\n", brd->name)); +} + + +/* + * Physically forces the FEP5 card to reset itself. + */ +static void dgap_do_reset_board(struct board_t *brd) +{ + uchar check; + u32 check1; + u32 check2; + int i = 0; + + if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase || !brd->re_map_port) { + DPR_INIT(("dgap_do_reset_board() start. bad values. brd: %p mem: %p io: %p\n", + brd, brd ? brd->re_map_membase : 0, brd ? brd->re_map_port : 0)); + return; + } + + DPR_INIT(("dgap_do_reset_board() start. io: %p\n", brd->re_map_port)); + + /* 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) { + APR(("*** WARNING *** Board not resetting... Failing board.\n")); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + goto failed; + } + + /* + * 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)) { + APR(("*** Warning *** No memory at %p for board.\n", brd->re_map_membase)); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + goto failed; + } + + if (brd->state != BOARD_FAILED) + brd->state = FINISHED_RESET; + +failed: + DPR_INIT(("dgap_do_reset_board() finish\n")); +} + + +/* + * Sends a concentrator image into the FEP5 board. + */ +void dgap_do_conc_load(struct board_t *brd, uchar *uaddr, int len) +{ + char *vaddr; + u16 offset = 0; + 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); + + /* + * The image was already read into kernel space, + * we do NOT need a user space read here + */ + memcpy_toio((char *) to_dp, uaddr, sizeof(struct downld_t)); + + /* Tell card we have data for it */ + writew(0, vaddr + (DOWNREQ)); + + brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; +} + + +#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; + uchar byte1; + uchar 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); +} + + +/* + * Our board poller function. + */ +void dgap_poll_tasklet(unsigned long data) +{ + struct board_t *bd = (struct board_t *) data; + ulong lock_flags; + ulong lock_flags2; + char *vaddr; + u16 head, tail; + u16 *chk_addr; + u16 check = 0; + + if (!bd || (bd->magic != DGAP_BOARD_MAGIC)) { + APR(("dgap_poll_tasklet() - NULL or bad bd.\n")); + return; + } + + if (bd->inhibit_poller) + return; + + DGAP_LOCK(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 *eaddr = NULL; + + if (!bd->re_map_membase) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + if (!bd->re_map_port) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + + if (!bd->nasync) { + goto out; + } + + /* + * 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); + check = readw(chk_addr); + /* Nonzero if FEP is requesting concentrator image. */ + if (check) { + if (bd->conc_dl_status == NO_PENDING_CONCENTRATOR_REQUESTS) + bd->conc_dl_status = NEED_CONCENTRATOR; + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + + } + } + + eaddr = (struct ev_t *) (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) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_event(bd); + DGAP_LOCK(bd->bd_lock, lock_flags); + } + +out: + /* + * If board is doing interrupts, ACK the interrupt. + */ + if (bd && bd->intr_running) { + readb(bd->re_map_port + 2); + } + + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + + /* Our state machine to get the board up and running */ + + /* Reset board */ + if (bd->state == NEED_RESET) { + + /* Get VPD info */ + dgap_get_vpd(bd); + + dgap_do_reset_board(bd); + } + + /* Move to next state */ + if (bd->state == FINISHED_RESET) { + bd->state = NEED_CONFIG; + } + + if (bd->state == NEED_CONFIG) { + /* + * Match this board to a config the user created for us. + */ + bd->bd_config = dgap_find_config(bd->type, bd->pci_bus, bd->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 (bd->type == PAPORT8 && !bd->bd_config) { + bd->bd_config = dgap_find_config(PAPORT4, bd->pci_bus, bd->pci_slot); + } + + /* + * Register the ttys (if any) into the kernel. + */ + if (bd->bd_config) { + bd->state = FINISHED_CONFIG; + } + else { + bd->state = CONFIG_NOT_FOUND; + } + } + + /* Move to next state */ + if (bd->state == FINISHED_CONFIG) { + bd->state = NEED_DEVICE_CREATION; + } + + /* Move to next state */ + if (bd->state == NEED_DEVICE_CREATION) { + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + } + + /* Move to next state */ + if (bd->state == FINISHED_DEVICE_CREATION) { + bd->state = NEED_BIOS_LOAD; + } + + /* Move to next state */ + if (bd->state == NEED_BIOS_LOAD) { + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + } + + /* Wait for BIOS to test board... */ + if (bd->state == WAIT_BIOS_LOAD) { + dgap_do_wait_for_bios(bd); + } + + /* Move to next state */ + if (bd->state == FINISHED_BIOS_LOAD) { + bd->state = NEED_FEP_LOAD; + + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + } + + /* Wait for FEP to load on board... */ + if (bd->state == WAIT_FEP_LOAD) { + dgap_do_wait_for_fep(bd); + } + + + /* Move to next state */ + if (bd->state == FINISHED_FEP_LOAD) { + + /* + * Do tty device initialization. + */ + int rc = dgap_tty_init(bd); + + if (rc < 0) { + dgap_tty_uninit(bd); + APR(("Can't init tty devices (%d)\n", rc)); + bd->state = BOARD_FAILED; + bd->dpastatus = BD_NOFEP; + } + else { + bd->state = NEED_PROC_CREATION; + + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + } + } + + /* Move to next state */ + if (bd->state == FINISHED_PROC_CREATION) { + + bd->state = BOARD_READY; + bd->dpastatus = BD_RUNNING; + + /* + * If user requested the board to run in interrupt mode, + * go and set it up on the board. + */ + if (bd->intr_used) { + writew(1, (bd->re_map_membase + ENABLE_INTR)); + /* + * Tell the board to poll the UARTS as fast as possible. + */ + writew(FEPPOLL_MIN, (bd->re_map_membase + FEPPOLL)); + bd->intr_running = 1; + } + + /* Wake up anyone waiting for board state to change to ready */ + wake_up_interruptible(&bd->state_wait); + } + + DGAP_UNLOCK(bd->bd_lock, lock_flags); +} + + +/*======================================================================= + * + * 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. + * + *=======================================================================*/ +void dgap_cmdb(struct channel_t *ch, uchar cmd, uchar byte1, uchar byte2, uint ncmds) +{ + char *vaddr = NULL; + struct cm_t *cm_addr = NULL; + 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) { + DPR_CORE(("%s:%d board is in failed state.\n", __FILE__, __LINE__)); + 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 *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); + + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + DPR_CORE(("%s:%d pointers out of range, failing board!\n", __FILE__, __LINE__)); + ch->ch_bd->state = BOARD_FAILED; + return; + } + + /* + * Put the data in the circular command buffer. + */ + writeb(cmd, (char *) (vaddr + head + CMDSTART + 0)); + writeb((uchar) ch->ch_portnum, (char *) (vaddr + head + CMDSTART + 1)); + writeb(byte1, (char *) (vaddr + head + CMDSTART + 2)); + writeb(byte2, (char *) (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) { + DPR_CORE(("%s:%d failing board.\n",__FILE__, __LINE__)); + 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. + * + *=======================================================================*/ +void dgap_cmdw(struct channel_t *ch, uchar cmd, u16 word, uint ncmds) +{ + char *vaddr = NULL; + struct cm_t *cm_addr = NULL; + 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) { + DPR_CORE(("%s:%d board is failed!\n", __FILE__, __LINE__)); + 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 *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); + + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + DPR_CORE(("%s:%d Pointers out of range. Failing board.\n",__FILE__, __LINE__)); + ch->ch_bd->state = BOARD_FAILED; + return; + } + + /* + * Put the data in the circular command buffer. + */ + writeb(cmd, (char *) (vaddr + head + CMDSTART + 0)); + writeb((uchar) ch->ch_portnum, (char *) (vaddr + head + CMDSTART + 1)); + writew((u16) word, (char *) (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) { + DPR_CORE(("%s:%d Failing board.\n",__FILE__, __LINE__)); + 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 *vaddr = NULL; + struct cm_t *cm_addr = NULL; + 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) { + DPR_CORE(("%s:%d board is failed!\n", __FILE__, __LINE__)); + 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 *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); + + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + DPR_CORE(("%s:%d Pointers out of range. Failing board.\n",__FILE__, __LINE__)); + 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((uchar) 0xff, (char *) (vaddr + head + CMDSTART + 0)); + + writeb((uchar) ch->ch_portnum, (uchar *) (vaddr + head + CMDSTART + 1)); + writew((u16) cmd, (char *) (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, (char *) (vaddr + CMDSTART)); + } else { + writew((u16) word, (char *) (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) { + DPR_CORE(("%s:%d Failing board.\n",__FILE__, __LINE__)); + ch->ch_bd->state = BOARD_FAILED; + return; + } + udelay(10); + } +} + + +/*======================================================================= + * + * dgap_wmove - Write data to FEP buffer. + * + * ch - Pointer to channel structure. + * buf - Poiter to characters to be moved. + * cnt - Number of characters to move. + * + *=======================================================================*/ +void dgap_wmove(struct channel_t *ch, char *buf, uint cnt) +{ + int n; + char *taddr; + struct bs_t *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) { + DPR_CORE(("%s:%d pointer out of range", __FILE__, __LINE__)); + 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)); +} + +/* + * Retrives the current custom baud rate from FEP memory, + * and returns it back to the user. + * Returns 0 on error. + */ +uint dgap_get_custom_baud(struct channel_t *ch) +{ + uchar *vaddr; + ulong offset = 0; + uint value = 0; + + 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 = ((((*(unsigned short *)(vaddr + ECS_SEG)) << 4) + + (ch->ch_portnum * 0x28) + LINE_SPEED)); + + value = readw(vaddr + offset); + return (value); +} + + +/* + * Calls the firmware to reset this channel. + */ +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. + * + *=======================================================================*/ +int dgap_param(struct tty_struct *tty) +{ + struct ktermios *ts; + struct board_t *bd; + struct channel_t *ch; + struct bs_t *bs; + struct un_t *un; + u16 head; + u16 cflag; + u16 iflag; + uchar mval; + uchar hflow; + + if (!tty || tty->magic != TTY_MAGIC) { + return (-ENXIO); + } + + un = (struct un_t *) tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) { + return (-ENXIO); + } + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) { + return (-ENXIO); + } + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) { + return (-ENXIO); + } + + bs = ch->ch_bs; + if (bs == 0) { + return (-ENXIO); + } + + DPR_PARAM(("param start: tdev: %x cflags: %x oflags: %x iflags: %x\n", + ch->ch_tun.un_dev, ch->ch_c_cflag, ch->ch_c_oflag, ch->ch_c_iflag)); + + ts = tty->termios; + + /* + * 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 + */ + + DPR_PARAM(("param: Want %d speed\n", ch->ch_custom_speed)); + + 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_baud_info = ch->ch_custom_speed = dgap_get_custom_baud(ch); + + DPR_PARAM(("param: Got %d speed\n", 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->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 { + DPR_IOCTL(("baud indices were out of range (%d)(%d)", + iindex, jindex)); + 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_DEVICE_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, (uchar) 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); + + DPR_PARAM(("dgap_param: mval: %x ch_mforce: %x ch_mval: %x ch_mostat: %x\n", + mval, ch->ch_mforce, ch->ch_mval, ch->ch_mostat)); + + if (ch->ch_mostat ^ mval) { + ch->ch_mostat = mval; + + /* Okay to have channel and board locks held calling this */ + DPR_PARAM(("dgap_param: Sending SMODEM mval: %x\n", mval)); + dgap_cmdb(ch, SMODEM, (uchar) mval, D_RTS(ch)|D_DTR(ch), 0); + } + + /* + * Read modem signals, and then call carrier function. + */ + ch->ch_mistat = readb(&(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); + } + + DPR_PARAM(("param finish\n")); + + return (0); +} + + +/* + * dgap_parity_scan() + * + * Convert the FEP5 way of reporting parity errors and breaks into + * the Linux line discipline way. + */ +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; + + DPR_PSCAN(("dgap_parity_scan start\n")); + + 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) { + DPR_PSCAN(("dgap_parity_scan in 3rd char of ff seq. c: %x setting break.\n", c)); + ch->ch_err_break++; + *fout++ = TTY_BREAK; + } + else { + DPR_PSCAN(("dgap_parity_scan in 3rd char of ff seq. c: %x setting parity.\n", c)); + ch->ch_err_parity++; + *fout++ = TTY_PARITY; + } + } + else { + DPR_PSCAN(("%s:%d Logic Error.\n", __FILE__, __LINE__)); + } + + count += 1; + ch->pscan_state = 0; + } + } + *len = count; + DPR_PSCAN(("dgap_parity_scan finish\n")); +} + + + + +/*======================================================================= + * + * 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 *bs; + uchar *event; + uchar *vaddr = NULL; + struct ev_t *eaddr = NULL; + uint head; + uint tail; + int port; + int reason; + int modem; + int b1; + + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-ENXIO); + + DGAP_LOCK(bd->bd_lock, lock_flags); + + vaddr = bd->re_map_membase; + + if (!vaddr) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return (-ENXIO); + } + + eaddr = (struct ev_t *) (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) { + DPR_EVENT(("should be calling xxfail %d\n", __LINE__)); + /* Let go of board lock */ + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return (-ENXIO); + } + + /* + * Loop to process all the events in the buffer. + */ + while (tail != head) { + + /* + * Get interrupt information. + */ + + event = bd->re_map_membase + tail + EVSTART; + + port = event[0]; + reason = event[1]; + modem = event[2]; + b1 = event[3]; + + DPR_EVENT(("event: jiffies: %ld port: %d reason: %x modem: %x\n", + jiffies, port, reason, modem)); + + /* + * 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. + */ + DGAP_LOCK(ch->ch_lock, lock_flags2); + + bs = ch->ch_bs; + + if (!bs) { + DGAP_UNLOCK(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. + */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + dgap_input(ch); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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) { + + DPR_EVENT(("got IFBREAK\n")); + + if (ch->ch_tun.un_tty) { + /* A break has been indicated */ + ch->ch_err_break++; + tty_buffer_request_room(ch->ch_tun.un_tty, 1); + tty_insert_flip_char(ch->ch_tun.un_tty, 0, TTY_BREAK); + tty_flip_buffer_push(ch->ch_tun.un_tty); + } + } + + /* + * Process Transmit low. + */ + if (reason & IFTLW) { + + DPR_EVENT(("event: got low event\n")); + + if (ch->ch_tun.un_flags & UN_LOW) { + ch->ch_tun.un_flags &= ~UN_LOW; + + if (ch->ch_tun.un_flags & UN_ISOPEN) { + if ((ch->ch_tun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + ch->ch_tun.un_tty->ldisc->ops->write_wakeup) +#else + ch->ch_tun.un_tty->ldisc.ops->write_wakeup) +#endif + { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + (ch->ch_tun.un_tty->ldisc->ops->write_wakeup)(ch->ch_tun.un_tty); +#else + (ch->ch_tun.un_tty->ldisc.ops->write_wakeup)(ch->ch_tun.un_tty); +#endif + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + wake_up_interruptible(&ch->ch_tun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + + DPR_EVENT(("event: Got low event. jiffies: %lu\n", jiffies)); + } + } + + if (ch->ch_pun.un_flags & UN_LOW) { + ch->ch_pun.un_flags &= ~UN_LOW; + if (ch->ch_pun.un_flags & UN_ISOPEN) { + if ((ch->ch_pun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + ch->ch_pun.un_tty->ldisc->ops->write_wakeup) +#else + ch->ch_pun.un_tty->ldisc.ops->write_wakeup) +#endif + { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + (ch->ch_pun.un_tty->ldisc->ops->write_wakeup)(ch->ch_pun.un_tty); +#else + (ch->ch_pun.un_tty->ldisc.ops->write_wakeup)(ch->ch_pun.un_tty); +#endif + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + wake_up_interruptible(&ch->ch_pun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + } + + if (ch->ch_flags & CH_WLOW) { + ch->ch_flags &= ~CH_WLOW; + wake_up_interruptible(&ch->ch_flags_wait); + } + } + + /* + * Process Transmit empty. + */ + if (reason & IFTEM) { + DPR_EVENT(("event: got empty event\n")); + + if (ch->ch_tun.un_flags & UN_EMPTY) { + ch->ch_tun.un_flags &= ~UN_EMPTY; + if (ch->ch_tun.un_flags & UN_ISOPEN) { + if ((ch->ch_tun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + ch->ch_tun.un_tty->ldisc->ops->write_wakeup) +#else + ch->ch_tun.un_tty->ldisc.ops->write_wakeup) +#endif + { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + (ch->ch_tun.un_tty->ldisc->ops->write_wakeup)(ch->ch_tun.un_tty); +#else + (ch->ch_tun.un_tty->ldisc.ops->write_wakeup)(ch->ch_tun.un_tty); +#endif + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + wake_up_interruptible(&ch->ch_tun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + } + + if (ch->ch_pun.un_flags & UN_EMPTY) { + ch->ch_pun.un_flags &= ~UN_EMPTY; + if (ch->ch_pun.un_flags & UN_ISOPEN) { + if ((ch->ch_pun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + ch->ch_pun.un_tty->ldisc->ops->write_wakeup) +#else + ch->ch_pun.un_tty->ldisc.ops->write_wakeup) +#endif + { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + (ch->ch_pun.un_tty->ldisc->ops->write_wakeup)(ch->ch_pun.un_tty); +#else + (ch->ch_pun.un_tty->ldisc.ops->write_wakeup)(ch->ch_pun.un_tty); +#endif + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + wake_up_interruptible(&ch->ch_pun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + } + + + if (ch->ch_flags & CH_WEMPTY) { + ch->ch_flags &= ~CH_WEMPTY; + wake_up_interruptible(&ch->ch_flags_wait); + } + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + +next: + tail = (tail + 4) & (EVMAX - EVSTART - 4); + } + + writew(tail, &(eaddr->ev_tail)); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + return (0); +} diff --git a/drivers/staging/dgap/dgap_fep5.h b/drivers/staging/dgap/dgap_fep5.h new file mode 100644 index 000000000000..3a12ba5e3c2a --- /dev/null +++ b/drivers/staging/dgap/dgap_fep5.h @@ -0,0 +1,253 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + ************************************************************************ + *** FEP Version 5 dependent definitions + ************************************************************************/ + +#ifndef __DGAP_FEP5_H +#define __DGAP_FEP5_H + +/************************************************************************ + * FEP memory offsets + ************************************************************************/ +#define START 0x0004L /* Execution start address */ + +#define CMDBUF 0x0d10L /* Command (cm_t) structure offset */ +#define CMDSTART 0x0400L /* Start of command buffer */ +#define CMDMAX 0x0800L /* End of command buffer */ + +#define EVBUF 0x0d18L /* Event (ev_t) structure */ +#define EVSTART 0x0800L /* Start of event buffer */ +#define EVMAX 0x0c00L /* End of event buffer */ +#define FEP5_PLUS 0x0E40 /* ASCII '5' and ASCII 'A' is here */ +#define ECS_SEG 0x0E44 /* Segment of the extended channel structure */ +#define LINE_SPEED 0x10 /* Offset into ECS_SEG for line speed */ + /* if the fep has extended capabilities */ + +/* BIOS MAGIC SPOTS */ +#define ERROR 0x0C14L /* BIOS error code */ +#define SEQUENCE 0x0C12L /* BIOS sequence indicator */ +#define POSTAREA 0x0C00L /* POST complete message area */ + +/* FEP MAGIC SPOTS */ +#define FEPSTAT POSTAREA /* OS here when FEP comes up */ +#define NCHAN 0x0C02L /* number of ports FEP sees */ +#define PANIC 0x0C10L /* PANIC area for FEP */ +#define KMEMEM 0x0C30L /* Memory for KME use */ +#define CONFIG 0x0CD0L /* Concentrator configuration info */ +#define CONFIGSIZE 0x0030 /* configuration info size */ +#define DOWNREQ 0x0D00 /* Download request buffer pointer */ + +#define CHANBUF 0x1000L /* Async channel (bs_t) structs */ +#define FEPOSSIZE 0x1FFF /* 8K FEPOS */ + +#define XEMPORTS 0xC02 /* + * Offset in board memory where FEP5 stores + * how many ports it has detected. + * NOTE: FEP5 reports 64 ports when the user + * has the cable in EBI OUT instead of EBI IN. + */ + +#define FEPCLR 0x00 +#define FEPMEM 0x02 +#define FEPRST 0x04 +#define FEPINT 0x08 +#define FEPMASK 0x0e +#define FEPWIN 0x80 + +#define LOWMEM 0x0100 +#define HIGHMEM 0x7f00 + +#define FEPTIMEOUT 200000 + +#define ENABLE_INTR 0x0e04 /* Enable interrupts flag */ +#define FEPPOLL_MIN 1 /* minimum of 1 millisecond */ +#define FEPPOLL_MAX 20 /* maximum of 20 milliseconds */ +#define FEPPOLL 0x0c26 /* Fep event poll interval */ + +#define IALTPIN 0x0080 /* Input flag to swap DSR <-> DCD */ + +/************************************************************************ + * Command structure definition. + ************************************************************************/ +struct cm_t { + volatile unsigned short cm_head; /* Command buffer head offset */ + volatile unsigned short cm_tail; /* Command buffer tail offset */ + volatile unsigned short cm_start; /* start offset of buffer */ + volatile unsigned short cm_max; /* last offset of buffer */ +}; + +/************************************************************************ + * Event structure definition. + ************************************************************************/ +struct ev_t { + volatile unsigned short ev_head; /* Command buffer head offset */ + volatile unsigned short ev_tail; /* Command buffer tail offset */ + volatile unsigned short ev_start; /* start offset of buffer */ + volatile unsigned short ev_max; /* last offset of buffer */ +}; + +/************************************************************************ + * Download buffer structure. + ************************************************************************/ +struct downld_t { + uchar dl_type; /* Header */ + uchar dl_seq; /* Download sequence */ + ushort dl_srev; /* Software revision number */ + ushort dl_lrev; /* Low revision number */ + ushort dl_hrev; /* High revision number */ + ushort dl_seg; /* Start segment address */ + ushort dl_size; /* Number of bytes to download */ + uchar dl_data[1024]; /* Download data */ +}; + +/************************************************************************ + * Per channel buffer structure + ************************************************************************ + * Base Structure Entries Usage Meanings to Host * + * * + * W = read write R = read only * + * C = changed by commands only * + * U = unknown (may be changed w/o notice) * + ************************************************************************/ +struct bs_t { + volatile unsigned short tp_jmp; /* Transmit poll jump */ + volatile unsigned short tc_jmp; /* Cooked procedure jump */ + volatile unsigned short ri_jmp; /* Not currently used */ + volatile unsigned short rp_jmp; /* Receive poll jump */ + + volatile unsigned short tx_seg; /* W Tx segment */ + volatile unsigned short tx_head; /* W Tx buffer head offset */ + volatile unsigned short tx_tail; /* R Tx buffer tail offset */ + volatile unsigned short tx_max; /* W Tx buffer size - 1 */ + + volatile unsigned short rx_seg; /* W Rx segment */ + volatile unsigned short rx_head; /* W Rx buffer head offset */ + volatile unsigned short rx_tail; /* R Rx buffer tail offset */ + volatile unsigned short rx_max; /* W Rx buffer size - 1 */ + + volatile unsigned short tx_lw; /* W Tx buffer low water mark */ + volatile unsigned short rx_lw; /* W Rx buffer low water mark */ + volatile unsigned short rx_hw; /* W Rx buffer high water mark */ + volatile unsigned short incr; /* W Increment to next channel */ + + volatile unsigned short fepdev; /* U SCC device base address */ + volatile unsigned short edelay; /* W Exception delay */ + volatile unsigned short blen; /* W Break length */ + volatile unsigned short btime; /* U Break complete time */ + + volatile unsigned short iflag; /* C UNIX input flags */ + volatile unsigned short oflag; /* C UNIX output flags */ + volatile unsigned short cflag; /* C UNIX control flags */ + volatile unsigned short wfill[13]; /* U Reserved for expansion */ + + volatile unsigned char num; /* U Channel number */ + volatile unsigned char ract; /* U Receiver active counter */ + volatile unsigned char bstat; /* U Break status bits */ + volatile unsigned char tbusy; /* W Transmit busy */ + volatile unsigned char iempty; /* W Transmit empty event enable */ + volatile unsigned char ilow; /* W Transmit low-water event enable */ + volatile unsigned char idata; /* W Receive data interrupt enable */ + volatile unsigned char eflag; /* U Host event flags */ + + volatile unsigned char tflag; /* U Transmit flags */ + volatile unsigned char rflag; /* U Receive flags */ + volatile unsigned char xmask; /* U Transmit ready flags */ + volatile unsigned char xval; /* U Transmit ready value */ + volatile unsigned char m_stat; /* RC Modem status bits */ + volatile unsigned char m_change; /* U Modem bits which changed */ + volatile unsigned char m_int; /* W Modem interrupt enable bits */ + volatile unsigned char m_last; /* U Last modem status */ + + volatile unsigned char mtran; /* C Unreported modem trans */ + volatile unsigned char orun; /* C Buffer overrun occurred */ + volatile unsigned char astartc; /* W Auxiliary Xon char */ + volatile unsigned char astopc; /* W Auxiliary Xoff char */ + volatile unsigned char startc; /* W Xon character */ + volatile unsigned char stopc; /* W Xoff character */ + volatile unsigned char vnextc; /* W Vnext character */ + volatile unsigned char hflow; /* C Software flow control */ + + volatile unsigned char fillc; /* U Delay Fill character */ + volatile unsigned char ochar; /* U Saved output character */ + volatile unsigned char omask; /* U Output character mask */ + + volatile unsigned char bfill[13]; /* U Reserved for expansion */ + + volatile unsigned char scc[16]; /* U SCC registers */ +}; + + +/************************************************************************ + * FEP supported functions + ************************************************************************/ +#define SRLOW 0xe0 /* Set receive low water */ +#define SRHIGH 0xe1 /* Set receive high water */ +#define FLUSHTX 0xe2 /* Flush transmit buffer */ +#define PAUSETX 0xe3 /* Pause data transmission */ +#define RESUMETX 0xe4 /* Resume data transmission */ +#define SMINT 0xe5 /* Set Modem Interrupt */ +#define SAFLOWC 0xe6 /* Set Aux. flow control chars */ +#define SBREAK 0xe8 /* Send break */ +#define SMODEM 0xe9 /* Set 8530 modem control lines */ +#define SIFLAG 0xea /* Set UNIX iflags */ +#define SFLOWC 0xeb /* Set flow control characters */ +#define STLOW 0xec /* Set transmit low water mark */ +#define RPAUSE 0xee /* Pause recieve */ +#define RRESUME 0xef /* Resume receive */ +#define CHRESET 0xf0 /* Reset Channel */ +#define BUFSETALL 0xf2 /* Set Tx & Rx buffer size avail*/ +#define SOFLAG 0xf3 /* Set UNIX oflags */ +#define SHFLOW 0xf4 /* Set hardware handshake */ +#define SCFLAG 0xf5 /* Set UNIX cflags */ +#define SVNEXT 0xf6 /* Set VNEXT character */ +#define SPINTFC 0xfc /* Reserved */ +#define SCOMMODE 0xfd /* Set RS232/422 mode */ + + +/************************************************************************ + * Modes for SCOMMODE + ************************************************************************/ +#define MODE_232 0x00 +#define MODE_422 0x01 + + +/************************************************************************ + * Event flags. + ************************************************************************/ +#define IFBREAK 0x01 /* Break received */ +#define IFTLW 0x02 /* Transmit low water */ +#define IFTEM 0x04 /* Transmitter empty */ +#define IFDATA 0x08 /* Receive data present */ +#define IFMODEM 0x20 /* Modem status change */ + +/************************************************************************ + * Modem flags + ************************************************************************/ +# define DM_RTS 0x02 /* Request to send */ +# define DM_CD 0x80 /* Carrier detect */ +# define DM_DSR 0x20 /* Data set ready */ +# define DM_CTS 0x10 /* Clear to send */ +# define DM_RI 0x40 /* Ring indicator */ +# define DM_DTR 0x01 /* Data terminal ready */ + + +#endif diff --git a/drivers/staging/dgap/dgap_kcompat.h b/drivers/staging/dgap/dgap_kcompat.h new file mode 100644 index 000000000000..8ebf4b7373b7 --- /dev/null +++ b/drivers/staging/dgap/dgap_kcompat.h @@ -0,0 +1,93 @@ +/* + * Copyright 2004 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + ************************************************************************* + * + * This file is intended to contain all the kernel "differences" between the + * various kernels that we support. + * + *************************************************************************/ + +#ifndef __DGAP_KCOMPAT_H +#define __DGAP_KCOMPAT_H + +# ifndef KERNEL_VERSION +# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +# endif + + +#if !defined(TTY_FLIPBUF_SIZE) +# define TTY_FLIPBUF_SIZE 512 +#endif + + +/* Sparse stuff */ +# ifndef __user +# define __user +# define __kernel +# define __safe +# define __force +# define __chk_user_ptr(x) (void)0 +# endif + + +# define PARM_STR(VAR, INIT, PERM, DESC) \ + static char *VAR = INIT; \ + char *dgap_##VAR; \ + module_param(VAR, charp, PERM); \ + MODULE_PARM_DESC(VAR, DESC); + +# define PARM_INT(VAR, INIT, PERM, DESC) \ + static int VAR = INIT; \ + int dgap_##VAR; \ + module_param(VAR, int, PERM); \ + MODULE_PARM_DESC(VAR, DESC); + +# define PARM_ULONG(VAR, INIT, PERM, DESC) \ + static ulong VAR = INIT; \ + ulong dgap_##VAR; \ + module_param(VAR, long, PERM); \ + MODULE_PARM_DESC(VAR, DESC); + + + + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) + + + + +/* NOTHING YET */ + + + + +# else + + + +# error "this driver does not support anything below the 2.6.27 kernel series." + + + +# endif + +#endif /* ! __DGAP_KCOMPAT_H */ diff --git a/drivers/staging/dgap/dgap_mgmt.c b/drivers/staging/dgap/dgap_mgmt.c new file mode 100644 index 000000000000..00624816d252 --- /dev/null +++ b/drivers/staging/dgap/dgap_mgmt.c @@ -0,0 +1,768 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + */ + +/************************************************************************ + * + * This file implements the mgmt functionality for the + * FEP5 based product lines. + * + ************************************************************************ + * $Id: dgap_mgmt.c,v 1.2 2010/12/13 19:38:04 markh Exp $ + */ + +#include +#include +#include +#include /* For jiffies, task states */ +#include /* For tasklet and interrupt structs/defines */ +#include +#include +#include +#include /* For copy_from_user/copy_to_user */ +#include /* For read[bwl]/write[bwl] */ + +#include "dgap_driver.h" +#include "dgap_kcompat.h" /* Kernel 2.4/2.6 compat includes */ +#include "dgap_fep5.h" +#include "dgap_parse.h" +#include "dgap_mgmt.h" +#include "dgap_downld.h" +#include "dgap_tty.h" +#include "dgap_proc.h" +#include "dgap_sysfs.h" + + +/* This holds the status of the KME buffer */ +static int dgap_kmebusy = 0; + +/* Our "in use" variables, to enforce 1 open only */ +static int dgap_mgmt_in_use = 0; +static int dgap_downld_in_use = 0; + + +/* + * dgap_mgmt_open() + * + * Open the mgmt/downld/dpa device + */ +int dgap_mgmt_open(struct inode *inode, struct file *file) +{ + unsigned long lock_flags; + unsigned int minor = iminor(inode); + + DPR_MGMT(("dgap_mgmt_open start.\n")); + + DGAP_LOCK(dgap_global_lock, lock_flags); + + /* mgmt device */ + if (minor == MGMT_MGMT) { + /* Only allow 1 open at a time on mgmt device */ + if (dgap_mgmt_in_use) { + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return (-EBUSY); + } + dgap_mgmt_in_use++; + } + /* downld device */ + else if (minor == MGMT_DOWNLD) { + /* Only allow 1 open at a time on downld device */ + if (dgap_downld_in_use) { + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return (-EBUSY); + } + dgap_downld_in_use++; + } + else { + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return (-ENXIO); + } + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + DPR_MGMT(("dgap_mgmt_open finish.\n")); + + return 0; +} + +/* + * dgap_mgmt_close() + * + * Open the mgmt/dpa device + */ +int dgap_mgmt_close(struct inode *inode, struct file *file) +{ + unsigned long lock_flags; + unsigned int minor = iminor(inode); + + DPR_MGMT(("dgap_mgmt_close start.\n")); + + DGAP_LOCK(dgap_global_lock, lock_flags); + + /* mgmt device */ + if (minor == MGMT_MGMT) { + if (dgap_mgmt_in_use) { + dgap_mgmt_in_use = 0; + } + } + /* downld device */ + else if (minor == MGMT_DOWNLD) { + if (dgap_downld_in_use) { + dgap_downld_in_use = 0; + } + } + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + DPR_MGMT(("dgap_mgmt_close finish.\n")); + + return 0; +} + + +/* + * dgap_mgmt_ioctl() + * + * ioctl the mgmt/dpa device + */ +#ifdef HAVE_UNLOCKED_IOCTL +long dgap_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file->f_dentry->d_inode; +#else +int dgap_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ +#endif + unsigned long lock_flags; + int error = 0; + int i = 0; + int j = 0; + struct board_t *brd; + struct channel_t *ch; + static struct downldio dlio; + void __user *uarg = (void __user *) arg; + + DPR_MGMT(("dgap_mgmt_ioctl start.\n")); + + switch (cmd) { + + /* HiJack the usage of SEDELAY to turn on/off debugging. */ + case DIGI_SEDELAY: + { + unsigned int value = 0; + + if (copy_from_user((unsigned int *) &value, uarg, sizeof(unsigned int))) { + return (-EFAULT); + } + + printk("Setting debug of value: %x\n", value); + dgap_debug = value; + return 0; + } + + case DIGI_DLREQ_GET: + { + +get_service: + + DGAP_LOCK(dgap_global_lock, lock_flags); + + if (dgap_driver_state == DRIVER_NEED_CONFIG_LOAD) { + + dgap_driver_state = DRIVER_REQUESTED_CONFIG; + + dlio.req_type = DLREQ_CONFIG; + dlio.bdid = 0; + dlio.image.fi.type = 0; + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + if (copy_to_user(uarg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(dgap_global_lock, lock_flags); + dgap_driver_state = DRIVER_NEED_CONFIG_LOAD; + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return(-EFAULT); + } + + return(0); + } + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + /* + * Loop thru each board. + * Check state, force state machine to start running. + */ + for (i = 0; i < dgap_NumBoards; i++ ) { + + brd = dgap_Board[i]; + + DGAP_LOCK(brd->bd_lock, lock_flags); + + switch (brd->state) { + + case NEED_DEVICE_CREATION: + + /* + * Let go of lock, tty_register() (and us also) + * does a non-atomic malloc, so it would be + * possible to deadlock the system if the + * malloc went to sleep. + */ + DGAP_UNLOCK(brd->bd_lock, lock_flags); + dgap_tty_register(brd); + dgap_finalize_board_init(brd); + DGAP_LOCK(brd->bd_lock, lock_flags); + + brd->state = REQUESTED_DEVICE_CREATION; + + dlio.req_type = DLREQ_DEVCREATE; + dlio.bdid = i; + dlio.image.fi.type = brd->dpatype; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + if (copy_to_user(uarg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = NEED_DEVICE_CREATION; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + + return(0); + + case NEED_BIOS_LOAD: + + brd->state = REQUESTED_BIOS; + + dlio.req_type = DLREQ_BIOS; + dlio.bdid = i; + dlio.image.fi.type = brd->dpatype; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + if (copy_to_user(uarg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = NEED_BIOS_LOAD; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + + return(0); + + case NEED_FEP_LOAD: + brd->state = REQUESTED_FEP; + + dlio.req_type = DLREQ_FEP; + dlio.bdid = i; + dlio.image.fi.type = brd->dpatype; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + if (copy_to_user(uarg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = NEED_FEP_LOAD; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + return(0); + + case NEED_PROC_CREATION: + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + dgap_proc_register_channel_postscan(brd->boardnum); + + ch = brd->channels[0]; + for (j = 0; j < brd->nasync; j++, ch = brd->channels[j]) { + struct device *classp; + classp = + tty_register_device(brd->SerialDriver, j, + &(ch->ch_bd->pdev->dev)); + ch->ch_tun.un_sysfs = classp; + dgap_create_tty_sysfs(&ch->ch_tun, classp); + + classp = + tty_register_device(brd->PrintDriver, j, + &(ch->ch_bd->pdev->dev)); + ch->ch_pun.un_sysfs = classp; + dgap_create_tty_sysfs(&ch->ch_pun, classp); + } + dgap_create_ports_sysfiles(brd); + + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = FINISHED_PROC_CREATION; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + + default: + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + + } + + DGAP_LOCK(brd->bd_lock, lock_flags); + + switch (brd->conc_dl_status) { + + case NEED_CONCENTRATOR: + { + u16 offset = 0; + char *vaddr; + struct downld_t *to_dp; + + vaddr = brd->re_map_membase; + if (!vaddr) { + brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + dlio.req_type = DLREQ_CONC; + dlio.bdid = i; + + offset = readw((u16 *) (vaddr + DOWNREQ)); + to_dp = (struct downld_t *) (vaddr + (int) offset); + + if (!to_dp) { + brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + memcpy(&dlio.image.dl, to_dp, sizeof(struct downld_t)); + + brd->conc_dl_status = REQUESTED_CONCENTRATOR; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + if (copy_to_user(uarg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->conc_dl_status = NEED_CONCENTRATOR; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + + return(0); + } + + default: + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + } + + /* + * Go to sleep waiting for the driver to signal an event to us. + */ + error = wait_event_interruptible(dgap_dl_wait, (dgap_dl_action)); + + DGAP_LOCK(dgap_dl_lock, lock_flags); + dgap_dl_action = 0; + DGAP_UNLOCK(dgap_dl_lock, lock_flags); + + /* Break out of ioctl if user cancelled us */ + if (error) + break; + + goto get_service; + + } + + case DIGI_DLREQ_SET: + { + uchar __user *uaddr = NULL; + uchar *uaddr2 = NULL; + + if (copy_from_user((char *) &dlio, uarg, sizeof(struct downldio))) { + return (-EFAULT); + } + + if (dlio.req_type == DLREQ_CONFIG) { + + uaddr = uarg + + (int) ( ((struct downldio *)0)->image.fi.fepimage); + + dgap_do_config_load(uaddr, dlio.image.fi.len); + dgap_after_config_loaded(); + + DGAP_LOCK(dgap_global_lock, lock_flags); + dgap_driver_state = DRIVER_READY; + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + break; + + } + + if (dlio.bdid < 0 || dlio.bdid > dgap_NumBoards) { + return(-ENXIO); + } + + brd = dgap_Board[dlio.bdid]; + + switch(dlio.req_type) { + + case DLREQ_BIOS: + if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) { + break; + } + + if (dlio.image.fi.type == -1) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOBIOS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + + uaddr = uarg + + (int) ( ((struct downldio *)0)->image.fi.fepimage); + + dgap_do_bios_load(brd, uaddr, dlio.image.fi.len); + + break; + case DLREQ_FEP: + if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) { + break; + } + + if (dlio.image.fi.type == -1) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOBIOS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + uaddr = uarg + + (int) ( ((struct downldio *)0)->image.fi.fepimage); + + dgap_do_fep_load(brd, uaddr, dlio.image.fi.len); + + break; + + case DLREQ_CONC: + + if (brd->state == BOARD_FAILED) { + break; + } + + if (dlio.image.fi.type == -1) { + break; + } + + uaddr2 = (char *) &dlio.image.dl; + dgap_do_conc_load(brd, uaddr2, sizeof(struct downld_t)); + + break; + + case DLREQ_DEVCREATE: + if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) { + break; + } + + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = FINISHED_DEVICE_CREATION; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + break; + + } + + break; + } + + + case DIGI_GETDD: + { + /* + * This returns the total number of boards + * in the system, as well as driver version + * and has space for a reserved entry + */ + struct digi_dinfo ddi; + + DGAP_LOCK(dgap_global_lock, lock_flags); + + ddi.dinfo_nboards = dgap_NumBoards; + sprintf(ddi.dinfo_version, "%s", DG_PART); + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + DPR_MGMT(("DIGI_GETDD returning numboards: %lu version: %s\n", + ddi.dinfo_nboards, ddi.dinfo_version)); + + if (copy_to_user(uarg, &ddi, sizeof(ddi))) + return(-EFAULT); + + break; + } + + case DIGI_GETBD: + { + int brd2; + struct digi_info di; + + if (copy_from_user(&brd2, uarg, sizeof(int))) { + return(-EFAULT); + } + + DPR_MGMT(("DIGI_GETBD asking about board: %d\n", brd2)); + + if ((brd2 < 0) || (brd2 > dgap_NumBoards) || (dgap_NumBoards == 0)) + return (-ENODEV); + + memset(&di, 0, sizeof(di)); + + di.info_bdnum = brd2; + + DGAP_LOCK(dgap_Board[brd2]->bd_lock, lock_flags); + + di.info_bdtype = dgap_Board[brd2]->dpatype; + di.info_bdstate = dgap_Board[brd2]->dpastatus; + di.info_ioport = (ulong) dgap_Board[brd2]->port; + di.info_physaddr = (ulong) dgap_Board[brd2]->membase; + di.info_physsize = (ulong) dgap_Board[brd2]->membase - dgap_Board[brd2]->membase_end; + if (dgap_Board[brd2]->state != BOARD_FAILED) + di.info_nports = dgap_Board[brd2]->nasync; + else + di.info_nports = 0; + + DGAP_UNLOCK(dgap_Board[brd2]->bd_lock, lock_flags); + + DPR_MGMT(("DIGI_GETBD returning type: %x state: %x ports: %x size: %lx\n", + di.info_bdtype, di.info_bdstate, di.info_nports, di.info_physsize)); + + if (copy_to_user(uarg, &di, sizeof (di))) + return(-EFAULT); + + break; + } + + case DIGI_KME: + { + int itmp, jtmp; + unchar *memaddr = NULL; + struct rw_t kme; + struct rw_t *mp = NULL; + int brd2 = 0; + struct board_t *bd; + + /* This ioctl takes an argument of type 'rw_t' + * and uses it to interact with the KME struct + * located on the digiboard itself. + */ + if (copy_from_user(&kme, uarg, sizeof(kme))) + return(-EFAULT); + + if (kme.rw_size > 128) + kme.rw_size = 128; + + brd2 = kme.rw_board; + + DPR_MGMT(("dgap_mgmt: DIGI_KME: %s asked for board %d\n", current->comm, brd2)); + + /* Sanity Checking... */ + if ((brd2 < 0) || (brd2 > dgap_NumBoards) || (dgap_NumBoards == 0)) + return (-ENODEV); + + bd = dgap_Board[brd2]; + + DGAP_LOCK(dgap_Board[brd2]->bd_lock, lock_flags); + + if (bd->state != BOARD_READY) { + DGAP_UNLOCK(dgap_Board[brd2]->bd_lock, lock_flags); + return(-ENODEV); + } + + memaddr = bd->re_map_membase; + + DGAP_UNLOCK(dgap_Board[brd2]->bd_lock, lock_flags); + + /* If the concentrator number is 0... */ + if (kme.rw_conc == 0 && kme.rw_addr < 0x100000) { + int page = 0; + int addr = kme.rw_addr; + int size = kme.rw_size; + caddr_t data = (caddr_t) kme.rw_data; + + while ((itmp = size)) { + + switch (kme.rw_req) { + case RW_READ: + { + register caddr_t cp1 = (char *)memaddr + addr; + register caddr_t cp2 = kme.rw_data; + + DPR_MGMT(("RW_READ CARDMEM - page=%d rw_addr=0x%lx rw_size=%x\n", + page, kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < itmp; jtmp++) { + *cp2++ = readb(cp1++); + } + } + + break; + + case RW_WRITE: + { + register caddr_t cp1 = memaddr + addr; + register caddr_t cp2 = data; + + DPR_MGMT(("RW_WRITE CARDMEM - page=%d rw_addr=0x%lx rw_size=%d\n", + page, kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < itmp; jtmp++) { + writeb(*cp2++, cp1++); + } + } + break; + } + + addr += itmp; + data += itmp; + size -= itmp; + + } + + } + else { + + /* + * Read/Write memory in a REMOTE CONCENTRATOR.. + * There is only 1 buffer, so do mutual + * exclusion to make sure only one KME + * request is pending... + */ + + mp = (struct rw_t *) (memaddr + KMEMEM); + + while (dgap_kmebusy != 0) { + dgap_kmebusy = 2; + error = wait_event_interruptible(bd->kme_wait, (!dgap_kmebusy)); + if (error) + goto endkme; + } + + dgap_kmebusy = 1; + + /* Copy KME request to the board.. */ + + mp->rw_board = kme.rw_board; + mp->rw_conc = kme.rw_conc; + mp->rw_reserved = kme.rw_reserved; + memcpy(&mp->rw_addr, &kme.rw_addr, sizeof(int)); + memcpy(&mp->rw_size, &kme.rw_size, sizeof(short)); + + if(kme.rw_req == RW_WRITE) { + register caddr_t cp1 = (caddr_t) mp->rw_data; + register caddr_t cp2 = (caddr_t) kme.rw_data; + + DPR_MGMT(("RW_WRITE CONCMEM - rw_addr=0x%lx rw_size=%d\n", + kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < (int) kme.rw_size; jtmp++) { + writeb(*cp2++, cp1++); + } + } + + /* EXECUTE REQUEST */ + + mp->rw_req = kme.rw_req; + + /* + * Wait for the board to process the + * request, but limit the wait to 2 secs + */ + + for (itmp = jiffies + (2 * HZ); mp->rw_req;) { + if(jiffies >= itmp) { + error = ENXIO; + /* Set request back to 0.. */ + mp->rw_req = 0; + goto endkme; + } + + schedule_timeout(HZ / 10); + } + + /* + * Since this portion of code is looksee + * ported from the HPUX EMUX code, i'm + * leaving OUT a portion of that code where + * the HP/UX code actually puts the process + * to sleep for some reason + */ + if (mp->rw_size < kme.rw_size) + memcpy(&kme.rw_size, &mp->rw_size, sizeof(short)); + + /* Copy the READ data back to the source buffer... */ + if (kme.rw_req == RW_READ) { + register caddr_t cp1 = (caddr_t) mp->rw_data; + register caddr_t cp2 = (caddr_t) kme.rw_data; + + DPR_MGMT(("RW_READ CONCMEM - rw_addr=0x%lx rw_size=%d\n", + kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < (int) kme.rw_size; jtmp++) { + *cp2++ = readb(cp1++); + } + } + + /* + * Common exit point for code sharing the + * kme buffer. Before exiting, always wake + * another process waiting for the buffer + */ + + endkme: + + if (dgap_kmebusy != 1) + wake_up_interruptible(&bd->kme_wait); + dgap_kmebusy = 0; + if (error == ENXIO) + return(-EINVAL); + } + + /* Copy the whole (Possibly Modified) mess */ + /* back out to user space... */ + + if (!error) { + if (copy_to_user(uarg, &kme, sizeof(kme))) + return (-EFAULT); + + return(0); + } + } + } + + DPR_MGMT(("dgap_mgmt_ioctl finish.\n")); + + return 0; +} diff --git a/drivers/staging/dgap/dgap_mgmt.h b/drivers/staging/dgap/dgap_mgmt.h new file mode 100644 index 000000000000..69e760314db7 --- /dev/null +++ b/drivers/staging/dgap/dgap_mgmt.h @@ -0,0 +1,38 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_MGMT_H +#define __DGAP_MGMT_H + +#define MGMT_MGMT 0 +#define MGMT_DOWNLD 1 + +int dgap_mgmt_open(struct inode *inode, struct file *file); +int dgap_mgmt_close(struct inode *inode, struct file *file); + +#ifdef HAVE_UNLOCKED_IOCTL +long dgap_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +#else +int dgap_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +#endif + +#endif + diff --git a/drivers/staging/dgap/dgap_parse.c b/drivers/staging/dgap/dgap_parse.c new file mode 100644 index 000000000000..d6d507bb05a4 --- /dev/null +++ b/drivers/staging/dgap/dgap_parse.c @@ -0,0 +1,1372 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + * + ***************************************************************************** + * + * dgap_parse.c - Parses the configuration information from the input file. + * + * $Id: dgap_parse.c,v 1.1 2009/10/23 14:01:57 markh Exp $ + * + */ +#include +#include +#include +#include + +#include "dgap_types.h" +#include "dgap_fep5.h" +#include "dgap_driver.h" +#include "dgap_conf.h" + + +/* + * Function prototypes. + */ +static int dgap_gettok(char **in, struct cnode *p); +static char *dgap_getword(char **in); +static char *dgap_savestring(char *s); +static struct cnode *dgap_newnode(int t); +static int dgap_checknode(struct cnode *p); +static void dgap_err(char *s); + +/* + * Our needed internal static variables... + */ +static struct cnode dgap_head; +#define MAXCWORD 200 +static char dgap_cword[MAXCWORD]; + +struct toklist { + int token; + char *string; +}; + +static struct toklist dgap_tlist[] = { + { BEGIN, "config_begin" }, + { END, "config_end" }, + { BOARD, "board" }, + { PCX, "Digi_AccelePort_C/X_PCI" }, /* C/X_PCI */ + { PEPC, "Digi_AccelePort_EPC/X_PCI" }, /* EPC/X_PCI */ + { PPCM, "Digi_AccelePort_Xem_PCI" }, /* PCI/Xem */ + { 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)" }, + { IO, "io" }, + { 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 } +}; + + +/* + * Parse a configuration file read into memory as a string. + */ +int dgap_parsefile(char **in, int Remove) +{ + struct cnode *p, *brd, *line, *conc; + int rc; + char *s = NULL, *s2 = NULL; + int linecnt = 0; + + p = &dgap_head; + brd = line = conc = NULL; + + /* perhaps we are adding to an existing list? */ + while (p->next != NULL) { + p = p->next; + } + + /* file must start with a BEGIN */ + while ( (rc = dgap_gettok(in,p)) != BEGIN ) { + if (rc == 0) { + dgap_err("unexpected EOF"); + return(-1); + } + } + + for (; ; ) { + rc = dgap_gettok(in,p); + if (rc == 0) { + dgap_err("unexpected EOF"); + return(-1); + } + + switch (rc) { + case 0: + dgap_err("unexpected end of file"); + return(-1); + + case BEGIN: /* should only be 1 begin */ + dgap_err("unexpected config_begin\n"); + return(-1); + + case END: + return(0); + + case BOARD: /* board info */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(BNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + + p->u.board.status = dgap_savestring("No"); + line = conc = NULL; + brd = p; + linecnt = -1; + break; + + case APORT2_920P: /* AccelePort_4 */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_2r_920 string"); + return(-1); + } + p->u.board.type = APORT2_920P; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_2r_920 PCI to config...\n")); + break; + + case APORT4_920P: /* AccelePort_4 */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_4r_920 string"); + return(-1); + } + p->u.board.type = APORT4_920P; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_4r_920 PCI to config...\n")); + break; + + case APORT8_920P: /* AccelePort_8 */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_8r_920 string"); + return(-1); + } + p->u.board.type = APORT8_920P; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_8r_920 PCI to config...\n")); + break; + + case PAPORT4: /* AccelePort_4 PCI */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_4r(PCI) string"); + return(-1); + } + p->u.board.type = PAPORT4; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_4r PCI to config...\n")); + break; + + case PAPORT8: /* AccelePort_8 PCI */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_8r string"); + return(-1); + } + p->u.board.type = PAPORT8; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_8r PCI to config...\n")); + break; + + case PCX: /* PCI C/X */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_C/X_(PCI) string"); + return(-1); + } + p->u.board.type = PCX; + p->u.board.v_type = 1; + p->u.board.conc1 = 0; + p->u.board.conc2 = 0; + p->u.board.module1 = 0; + p->u.board.module2 = 0; + DPR_INIT(("Adding PCI C/X to config...\n")); + break; + + case PEPC: /* PCI EPC/X */ + if (p->type != BNODE) { + dgap_err("unexpected \"Digi_EPC/X_(PCI)\" string"); + return(-1); + } + p->u.board.type = PEPC; + p->u.board.v_type = 1; + p->u.board.conc1 = 0; + p->u.board.conc2 = 0; + p->u.board.module1 = 0; + p->u.board.module2 = 0; + DPR_INIT(("Adding PCI EPC/X to config...\n")); + break; + + case PPCM: /* PCI/Xem */ + if (p->type != BNODE) { + dgap_err("unexpected PCI/Xem string"); + return(-1); + } + p->u.board.type = PPCM; + p->u.board.v_type = 1; + p->u.board.conc1 = 0; + p->u.board.conc2 = 0; + DPR_INIT(("Adding PCI XEM to config...\n")); + break; + + case IO: /* i/o port */ + if (p->type != BNODE) { + dgap_err("IO port only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.portstr = dgap_savestring(s); + p->u.board.port = (short)simple_strtol(s, &s2, 0); + if ((short)strlen(s) > (short)(s2 - s)) { + dgap_err("bad number for IO port"); + return(-1); + } + p->u.board.v_port = 1; + DPR_INIT(("Adding IO (%s) to config...\n", s)); + break; + + case MEM: /* memory address */ + if (p->type != BNODE) { + dgap_err("memory address only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.addrstr = dgap_savestring(s); + p->u.board.addr = simple_strtoul(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for memory address"); + return(-1); + } + p->u.board.v_addr = 1; + DPR_INIT(("Adding MEM (%s) to config...\n", s)); + break; + + case PCIINFO: /* pci information */ + if (p->type != BNODE) { + dgap_err("memory address only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.pcibusstr = dgap_savestring(s); + p->u.board.pcibus = simple_strtoul(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for pci bus"); + return(-1); + } + p->u.board.v_pcibus = 1; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.pcislotstr = dgap_savestring(s); + p->u.board.pcislot = simple_strtoul(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for pci slot"); + return(-1); + } + p->u.board.v_pcislot = 1; + + DPR_INIT(("Adding PCIINFO (%s %s) to config...\n", p->u.board.pcibusstr, + p->u.board.pcislotstr)); + break; + + case METHOD: + if (p->type != BNODE) { + dgap_err("install method only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.method = dgap_savestring(s); + p->u.board.v_method = 1; + DPR_INIT(("Adding METHOD (%s) to config...\n", s)); + break; + + case STATUS: + if (p->type != BNODE) { + dgap_err("config status only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.status = dgap_savestring(s); + DPR_INIT(("Adding STATUS (%s) to config...\n", s)); + break; + + case NPORTS: /* number of ports */ + if (p->type == BNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.nport = (char)simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_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 == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.nport = (char)simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_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 == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.module.nport = (char)simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for number of ports"); + return(-1); + } + p->u.module.v_nport = 1; + } else { + dgap_err("nports only valid for concentrators or modules"); + return(-1); + } + DPR_INIT(("Adding NPORTS (%s) to config...\n", s)); + break; + + case ID: /* letter ID used in tty name */ + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + + p->u.board.status = dgap_savestring(s); + + if (p->type == CNODE) { + p->u.conc.id = dgap_savestring(s); + p->u.conc.v_id = 1; + } else if (p->type == MNODE) { + p->u.module.id = dgap_savestring(s); + p->u.module.v_id = 1; + } else { + dgap_err("id only valid for concentrators or modules"); + return(-1); + } + DPR_INIT(("Adding ID (%s) to config...\n", s)); + break; + + case STARTO: /* start offset of ID */ + if (p->type == BNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.start = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_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 == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.start = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_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 == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.module.start = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for start of tty count"); + return(-1); + } + p->u.module.v_start = 1; + } else { + dgap_err("start only valid for concentrators or modules"); + return(-1); + } + DPR_INIT(("Adding START (%s) to config...\n", s)); + break; + + case TTYN: /* tty name prefix */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(TNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if ( (s = dgap_getword(in)) == NULL ) { + dgap_err("unexpeced end of file"); + return(-1); + } + if ( (p->u.ttyname = dgap_savestring(s)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + DPR_INIT(("Adding TTY (%s) to config...\n", s)); + break; + + case CU: /* cu name prefix */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(CUNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if ( (s = dgap_getword(in)) == NULL ) { + dgap_err("unexpeced end of file"); + return(-1); + } + if ( (p->u.cuname = dgap_savestring(s)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + DPR_INIT(("Adding CU (%s) to config...\n", s)); + break; + + case LINE: /* line information */ + if (dgap_checknode(p)) + return(-1); + if (brd == NULL) { + dgap_err("must specify board before line info"); + return(-1); + } + switch (brd->u.board.type) { + case PPCM: + dgap_err("line not vaild for PC/em"); + return(-1); + } + if ( (p->next = dgap_newnode(LNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + conc = NULL; + line = p; + linecnt++; + DPR_INIT(("Adding LINE to config...\n")); + break; + + case CONC: /* concentrator information */ + if (dgap_checknode(p)) + return(-1); + if (line == NULL) { + dgap_err("must specify line info before concentrator"); + return(-1); + } + if ( (p->next = dgap_newnode(CNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + conc = p; + if (linecnt) + brd->u.board.conc2++; + else + brd->u.board.conc1++; + + DPR_INIT(("Adding CONC to config...\n")); + break; + + case CX: /* c/x type concentrator */ + if (p->type != CNODE) { + dgap_err("cx only valid for concentrators"); + return(-1); + } + p->u.conc.type = CX; + p->u.conc.v_type = 1; + DPR_INIT(("Adding CX to config...\n")); + break; + + case EPC: /* epc type concentrator */ + if (p->type != CNODE) { + dgap_err("cx only valid for concentrators"); + return(-1); + } + p->u.conc.type = EPC; + p->u.conc.v_type = 1; + DPR_INIT(("Adding EPC to config...\n")); + break; + + case MOD: /* EBI module */ + if (dgap_checknode(p)) + return(-1); + if (brd == NULL) { + dgap_err("must specify board info before EBI modules"); + return(-1); + } + switch (brd->u.board.type) { + case PPCM: + linecnt = 0; + break; + default: + if (conc == NULL) { + dgap_err("must specify concentrator info before EBI module"); + return(-1); + } + } + if ( (p->next = dgap_newnode(MNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if (linecnt) + brd->u.board.module2++; + else + brd->u.board.module1++; + + DPR_INIT(("Adding MOD to config...\n")); + break; + + case PORTS: /* ports type EBI module */ + if (p->type != MNODE) { + dgap_err("ports only valid for EBI modules"); + return(-1); + } + p->u.module.type = PORTS; + p->u.module.v_type = 1; + DPR_INIT(("Adding PORTS to config...\n")); + break; + + case MODEM: /* ports type EBI module */ + if (p->type != MNODE) { + dgap_err("modem only valid for modem modules"); + return(-1); + } + p->u.module.type = MODEM; + p->u.module.v_type = 1; + DPR_INIT(("Adding MODEM to config...\n")); + break; + + case CABLE: + if (p->type == LNODE) { + if ((s = dgap_getword(in)) == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.line.cable = dgap_savestring(s); + p->u.line.v_cable = 1; + } + DPR_INIT(("Adding CABLE (%s) to config...\n", s)); + break; + + case SPEED: /* sync line speed indication */ + if (p->type == LNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.line.speed = (char)simple_strtol(s, &s2, 0); + if ((short)strlen(s) > (short)(s2 - s)) { + dgap_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 == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.speed = (char)simple_strtol(s, &s2, 0); + if ((short)strlen(s) > (short)(s2 - s)) { + dgap_err("bad number for line speed"); + return(-1); + } + p->u.conc.v_speed = 1; + } else { + dgap_err("speed valid only for lines or concentrators."); + return(-1); + } + DPR_INIT(("Adding SPEED (%s) to config...\n", s)); + break; + + case CONNECT: + if (p->type == CNODE) { + if ((s = dgap_getword(in)) == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.connect = dgap_savestring(s); + p->u.conc.v_connect = 1; + } + DPR_INIT(("Adding CONNECT (%s) to config...\n", s)); + break; + case PRINT: /* transparent print name prefix */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(PNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if ( (s = dgap_getword(in)) == NULL ) { + dgap_err("unexpeced end of file"); + return(-1); + } + if ( (p->u.printname = dgap_savestring(s)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + DPR_INIT(("Adding PRINT (%s) to config...\n", s)); + break; + + case CMAJOR: /* major number */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(JNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.majornumber = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for major number"); + return(-1); + } + DPR_INIT(("Adding CMAJOR (%s) to config...\n", s)); + break; + + case ALTPIN: /* altpin setting */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(ANODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.altpin = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for altpin"); + return(-1); + } + DPR_INIT(("Adding ALTPIN (%s) to config...\n", s)); + break; + + case USEINTR: /* enable interrupt setting */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(INTRNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.useintr = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for useintr"); + return(-1); + } + DPR_INIT(("Adding USEINTR (%s) to config...\n", s)); + break; + + case TTSIZ: /* size of tty structure */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(TSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.ttysize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for ttysize"); + return(-1); + } + DPR_INIT(("Adding TTSIZ (%s) to config...\n", s)); + break; + + case CHSIZ: /* channel structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(CSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.chsize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for chsize"); + return(-1); + } + DPR_INIT(("Adding CHSIZE (%s) to config...\n", s)); + break; + + case BSSIZ: /* board structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(BSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.bssize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for bssize"); + return(-1); + } + DPR_INIT(("Adding BSSIZ (%s) to config...\n", s)); + break; + + case UNTSIZ: /* sched structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(USNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.unsize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for schedsize"); + return(-1); + } + DPR_INIT(("Adding UNTSIZ (%s) to config...\n", s)); + break; + + case F2SIZ: /* f2200 structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(FSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.f2size = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for f2200size"); + return(-1); + } + DPR_INIT(("Adding F2SIZ (%s) to config...\n", s)); + break; + + case VPSIZ: /* vpix structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(VSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.vpixsize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for vpixsize"); + return(-1); + } + DPR_INIT(("Adding VPSIZ (%s) to config...\n", s)); + break; + } + } +} + + +/* + * dgap_sindex: much like index(), but it looks for a match of any character in + * the group, and returns that position. If the first character is a ^, then + * this will match the first occurence not in that group. + */ +static char *dgap_sindex (char *string, char *group) +{ + char *ptr; + + if (!string || !group) + return (char *) NULL; + + if (*group == '^') { + group++; + for (; *string; string++) { + for (ptr = group; *ptr; ptr++) { + if (*ptr == *string) + break; + } + if (*ptr == '\0') + return string; + } + } + else { + for (; *string; string++) { + for (ptr = group; *ptr; ptr++) { + if (*ptr == *string) + return string; + } + } + } + + return (char *) NULL; +} + + +/* + * Get a token from the input file; return 0 if end of file is reached + */ +static int dgap_gettok(char **in, struct cnode *p) +{ + char *w; + struct toklist *t; + + if (strstr(dgap_cword, "boar")) { + 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); + } + } + dgap_err("board !!type not specified"); + return(1); + } + else { + while ( (w = dgap_getword(in)) != NULL ) { + snprintf(dgap_cword, MAXCWORD, "%s", w); + for (t = dgap_tlist; t->token != 0; t++) { + if ( !strcmp(w, t->string) ) + return(t->token); + } + } + return(0); + } +} + + +/* + * 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 = dgap_sindex(*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; +} + + +/* + * print an error message, giving the line number in the file where + * the error occurred. + */ +static void dgap_err(char *s) +{ + printk("DGAP: parse: %s\n", s); +} + + +/* + * allocate a new configuration node of type t + */ +static struct cnode *dgap_newnode(int t) +{ + struct cnode *n; + if ( (n = (struct cnode *) kmalloc(sizeof(struct cnode ), GFP_ATOMIC) ) != NULL) { + memset( (char *)n, 0, sizeof(struct cnode ) ); + n->type = t; + } + return(n); +} + + +/* + * 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 BNODE: + if (p->u.board.v_type == 0) { + dgap_err("board type !not specified"); + return(1); + } + + return(0); + + case LNODE: + if (p->u.line.v_speed == 0) { + dgap_err("line speed not specified"); + return(1); + } + return(0); + + case CNODE: + if (p->u.conc.v_type == 0) { + dgap_err("concentrator type not specified"); + return(1); + } + if (p->u.conc.v_speed == 0) { + dgap_err("concentrator line speed not specified"); + return(1); + } + if (p->u.conc.v_nport == 0) { + dgap_err("number of ports on concentrator not specified"); + return(1); + } + if (p->u.conc.v_id == 0) { + dgap_err("concentrator id letter not specified"); + return(1); + } + return(0); + + case MNODE: + if (p->u.module.v_type == 0) { + dgap_err("EBI module type not specified"); + return(1); + } + if (p->u.module.v_nport == 0) { + dgap_err("number of ports on EBI module not specified"); + return(1); + } + if (p->u.module.v_id == 0) { + dgap_err("EBI module id letter not specified"); + return(1); + } + return(0); + } + return(0); +} + +/* + * save a string somewhere + */ +static char *dgap_savestring(char *s) +{ + char *p; + if ( (p = kmalloc(strlen(s) + 1, GFP_ATOMIC) ) != NULL) { + strcpy(p, s); + } + return(p); +} + + +/* + * Given a board pointer, returns whether we should use interrupts or not. + */ +uint dgap_config_get_useintr(struct board_t *bd) +{ + struct cnode *p = NULL; + + if (!bd) + return(0); + + for (p = bd->bd_config; p; p = p->next) { + switch (p->type) { + case INTRNODE: + /* + * check for pcxr types. + */ + return p->u.useintr; + default: + break; + } + } + + /* If not found, then don't turn on interrupts. */ + return 0; +} + + +/* + * Given a board pointer, returns whether we turn on altpin or not. + */ +uint dgap_config_get_altpin(struct board_t *bd) +{ + struct cnode *p = NULL; + + if (!bd) + return(0); + + for (p = bd->bd_config; p; p = p->next) { + switch (p->type) { + case ANODE: + /* + * check for pcxr types. + */ + return p->u.altpin; + default: + break; + } + } + + /* 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 occurance in the list. + */ +struct cnode *dgap_find_config(int type, int bus, int slot) +{ + struct cnode *p, *prev = NULL, *prev2 = NULL, *found = NULL; + + p = &dgap_head; + + while (p->next != NULL) { + prev = p; + p = p->next; + + if (p->type == BNODE) { + + if (p->u.board.type == type) { + + if (p->u.board.v_pcibus && p->u.board.pcibus != bus) { + DPR(("Found matching board, but wrong bus position. System says bus %d, we want bus %ld\n", + bus, p->u.board.pcibus)); + continue; + } + if (p->u.board.v_pcislot && p->u.board.pcislot != slot) { + DPR_INIT(("Found matching board, but wrong slot position. System says slot %d, we want slot %ld\n", + slot, p->u.board.pcislot)); + continue; + } + + DPR_INIT(("Matched type in config file\n")); + + found = p; + /* + * Keep walking thru the list till we find the next board. + */ + while (p->next != NULL) { + prev2 = p; + p = p->next; + if (p->type == BNODE) { + + /* + * 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) + */ +uint dgap_config_get_number_of_ports(struct board_t *bd) +{ + int count = 0; + struct cnode *p = NULL; + + 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); +} + +char *dgap_create_config_string(struct board_t *bd, char *string) +{ + char *ptr = string; + struct cnode *p = NULL; + struct cnode *q = NULL; + 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 != NULL) && (q->type == MNODE) ) { + *ptr = (p->u.conc.nport + 0x80); + ptr++; + p = q; + while ((q->next != NULL) && (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; +} + + + +char *dgap_get_config_letters(struct board_t *bd, char *string) +{ + int found = FALSE; + char *ptr = string; + struct cnode *cptr = NULL; + int len = 0; + int left = MAXTTYNAMELEN; + + if (!bd) { + return ""; + } + + 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->type == TNODE && found == TRUE) { + char *ptr1; + if (strstr(cptr->u.ttyname, "tty")) { + ptr1 = cptr->u.ttyname; + ptr1 += 3; + } + else { + ptr1 = cptr->u.ttyname; + } + if (ptr1) { + len = snprintf(ptr, left, "%s", ptr1); + left -= len; + ptr += len; + if (left <= 0) + break; + } + } + + if (cptr->type == CNODE) { + if (cptr->u.conc.id) { + len = snprintf(ptr, left, "%s", cptr->u.conc.id); + left -= len; + ptr += len; + if (left <= 0) + break; + } + } + + if (cptr->type == MNODE) { + if (cptr->u.module.id) { + len = snprintf(ptr, left, "%s", cptr->u.module.id); + left -= len; + ptr += len; + if (left <= 0) + break; + } + } + } + + return string; +} diff --git a/drivers/staging/dgap/dgap_parse.h b/drivers/staging/dgap/dgap_parse.h new file mode 100644 index 000000000000..8128c47343cb --- /dev/null +++ b/drivers/staging/dgap/dgap_parse.h @@ -0,0 +1,35 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef _DGAP_PARSE_H +#define _DGAP_PARSE_H + +#include "dgap_driver.h" + +extern int dgap_parsefile(char **in, int Remove); +extern struct cnode *dgap_find_config(int type, int bus, int slot); +extern uint dgap_config_get_number_of_ports(struct board_t *bd); +extern char *dgap_create_config_string(struct board_t *bd, char *string); +extern char *dgap_get_config_letters(struct board_t *bd, char *string); +extern uint dgap_config_get_useintr(struct board_t *bd); +extern uint dgap_config_get_altpin(struct board_t *bd); + +#endif diff --git a/drivers/staging/dgap/dgap_pci.h b/drivers/staging/dgap/dgap_pci.h new file mode 100644 index 000000000000..05ed374f08e1 --- /dev/null +++ b/drivers/staging/dgap/dgap_pci.h @@ -0,0 +1,92 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +/* $Id: dgap_pci.h,v 1.1 2009/10/23 14:01:57 markh Exp $ */ + +#ifndef __DGAP_PCI_H +#define __DGAP_PCI_H + +#define PCIMAX 32 /* maximum number of PCI boards */ + +#define DIGI_VID 0x114F + +#define PCI_DEVICE_EPC_DID 0x0002 +#define PCI_DEVICE_XEM_DID 0x0004 +#define PCI_DEVICE_XR_DID 0x0005 +#define PCI_DEVICE_CX_DID 0x0006 +#define PCI_DEVICE_XRJ_DID 0x0009 /* PLX-based Xr adapter */ +#define PCI_DEVICE_XR_IBM_DID 0x0011 /* IBM 8-port Async Adapter */ +#define PCI_DEVICE_XR_BULL_DID 0x0013 /* BULL 8-port Async Adapter */ +#define PCI_DEVICE_XR_SAIP_DID 0x001c /* SAIP card - Xr adapter */ +#define PCI_DEVICE_XR_422_DID 0x0012 /* Xr-422 */ +#define PCI_DEVICE_920_2_DID 0x0034 /* XR-Plus 920 K, 2 port */ +#define PCI_DEVICE_920_4_DID 0x0026 /* XR-Plus 920 K, 4 port */ +#define PCI_DEVICE_920_8_DID 0x0027 /* XR-Plus 920 K, 8 port */ +#define PCI_DEVICE_EPCJ_DID 0x000a /* PLX 9060 chip for PCI */ +#define PCI_DEVICE_CX_IBM_DID 0x001b /* IBM 128-port Async Adapter */ +#define PCI_DEVICE_920_8_HP_DID 0x0058 /* HP XR-Plus 920 K, 8 port */ +#define PCI_DEVICE_XEM_HP_DID 0x0059 /* HP Xem PCI */ + +#define PCI_DEVICE_XEM_NAME "AccelePort XEM" +#define PCI_DEVICE_CX_NAME "AccelePort CX" +#define PCI_DEVICE_XR_NAME "AccelePort Xr" +#define PCI_DEVICE_XRJ_NAME "AccelePort Xr (PLX)" +#define PCI_DEVICE_XR_SAIP_NAME "AccelePort Xr (SAIP)" +#define PCI_DEVICE_920_2_NAME "AccelePort Xr920 2 port" +#define PCI_DEVICE_920_4_NAME "AccelePort Xr920 4 port" +#define PCI_DEVICE_920_8_NAME "AccelePort Xr920 8 port" +#define PCI_DEVICE_XR_422_NAME "AccelePort Xr 422" +#define PCI_DEVICE_EPCJ_NAME "AccelePort EPC (PLX)" +#define PCI_DEVICE_XR_BULL_NAME "AccelePort Xr (BULL)" +#define PCI_DEVICE_XR_IBM_NAME "AccelePort Xr (IBM)" +#define PCI_DEVICE_CX_IBM_NAME "AccelePort CX (IBM)" +#define PCI_DEVICE_920_8_HP_NAME "AccelePort Xr920 8 port (HP)" +#define PCI_DEVICE_XEM_HP_NAME "AccelePort XEM (HP)" + + +/* + * 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 + */ + +/* Potential location of PCI Bios from E0000 to FFFFF*/ +#define PCI_BIOS_SIZE 0x00020000 + +/* Size of Memory and I/O for PCI (4MB) */ +#define PCI_RAM_SIZE 0x00400000 + +/* Size of Memory (2MB) */ +#define PCI_MEM_SIZE 0x00200000 + +/* Max PCI Window Size (2MB) */ +#define PCI_WIN_SIZE 0x00200000 + +#define PCI_WIN_SHIFT 21 /* 21 bits max */ + +/* Offset of I/0 in Memory (2MB) */ +#define PCI_IO_OFFSET 0x00200000 + +/* Size of IO (2MB) */ +#define PCI_IO_SIZE 0x00200000 + +#endif diff --git a/drivers/staging/dgap/dgap_proc.c b/drivers/staging/dgap/dgap_proc.c new file mode 100644 index 000000000000..6950a6c9b87d --- /dev/null +++ b/drivers/staging/dgap/dgap_proc.c @@ -0,0 +1,1924 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + * + * $Id: dgap_proc.c,v 1.2 2010/12/22 21:22:39 markh Exp $ + */ + +#include +#include +#include /* For jiffies, task states */ +#include /* For tasklet and interrupt structs/defines */ +#include +#include +#include +#include +#include /* For in_egroup_p() */ +#include +#include /* For copy_from_user/copy_to_user */ +#include + +#include "dgap_driver.h" +#include "dgap_proc.h" +#include "dgap_mgmt.h" +#include "dgap_conf.h" +#include "dgap_parse.h" +#include "dgap_fep5.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) +#define init_MUTEX(sem) sema_init(sem, 1) +#endif + +/* The /proc/dgap directory */ +static struct proc_dir_entry *ProcDGAP; + + +/* File operation declarations */ +static int dgap_gen_proc_open(struct inode *, struct file *); +static int dgap_gen_proc_close(struct inode *, struct file *); +static ssize_t dgap_gen_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); +static ssize_t dgap_gen_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos); + +static int dgap_proc_chk_perm(struct inode *, int); + +static const struct file_operations dgap_proc_file_ops = +{ + .owner = THIS_MODULE, + .read = dgap_gen_proc_read, /* read */ + .write = dgap_gen_proc_write, /* write */ + .open = dgap_gen_proc_open, /* open */ + .release = dgap_gen_proc_close, /* release */ +}; + + +static struct inode_operations dgap_proc_inode_ops = +{ + .permission = dgap_proc_chk_perm +}; + + +static void dgap_register_proc_table(struct dgap_proc_entry *, struct proc_dir_entry *); +static void dgap_unregister_proc_table(struct dgap_proc_entry *, struct proc_dir_entry *); +static void dgap_remove_proc_entry(struct proc_dir_entry *pde); + + +/* Stuff in /proc/ */ +static int dgap_read_info(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_write_info(struct dgap_proc_entry *table, int dir, struct file *filp, + const char __user *buffer, ssize_t *lenp, loff_t *ppos); + +static int dgap_read_mknod(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); + +static struct dgap_proc_entry dgap_table[] = { + {DGAP_INFO, "info", 0600, NULL, NULL, NULL, &dgap_read_info, &dgap_write_info, + NULL, __SEMAPHORE_INITIALIZER(dgap_table[0].excl_sem, 1), 0, NULL }, + {DGAP_MKNOD, "mknod", 0600, NULL, NULL, NULL, &dgap_read_mknod, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_table[1].excl_sem, 1), 0, NULL }, + + {0} +}; + + +/* Stuff in /proc// */ +static int dgap_read_board_info(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_board_vpd(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_board_vpddata(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_board_mknod(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_board_ttystats(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_board_ttyflags(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); + +static struct dgap_proc_entry dgap_board_table[] = { + {DGAP_BOARD_INFO, "info", 0600, NULL, NULL, NULL, &dgap_read_board_info, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_board_table[0].excl_sem, 1), 0, NULL }, + {DGAP_BOARD_VPD, "vpd", 0600, NULL, NULL, NULL, &dgap_read_board_vpd, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_board_table[1].excl_sem, 1), 0, NULL }, + {DGAP_BOARD_VPDDATA, "vpddata", 0600, NULL, NULL, NULL, &dgap_read_board_vpddata, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_board_table[2].excl_sem, 1), 0, NULL }, + {DGAP_BOARD_TTYSTATS, "stats", 0600, NULL, NULL, NULL, &dgap_read_board_ttystats, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_board_table[3].excl_sem, 1), 0, NULL }, + {DGAP_BOARD_TTYFLAGS, "flags", 0600, NULL, NULL, NULL, &dgap_read_board_ttyflags, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_board_table[4].excl_sem, 1), 0, NULL }, + {DGAP_BOARD_MKNOD, "mknod", 0600, NULL, NULL, NULL, &dgap_read_board_mknod, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_board_table[5].excl_sem, 1), 0, NULL }, + {0} +}; + + +/* Stuff in /proc// */ +static int dgap_read_channel_info(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_open_channel_sniff(struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_close_channel_sniff(struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_channel_sniff(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_channel_custom_ttyname(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_channel_custom_prname(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); +static int dgap_read_channel_fepstate(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); + + +static struct dgap_proc_entry dgap_channel_table[] = { + {DGAP_PORT_INFO, "info", 0600, NULL, NULL, NULL, &dgap_read_channel_info, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_channel_table[0].excl_sem, 1), 0, NULL }, + {DGAP_PORT_SNIFF, "sniff", 0600, NULL, &dgap_open_channel_sniff, &dgap_close_channel_sniff, &dgap_read_channel_sniff, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_channel_table[1].excl_sem, 1), 0, NULL}, + {DGAP_PORT_CUSTOM_TTYNAME, "ttyname", 0600, NULL, NULL, NULL, &dgap_read_channel_custom_ttyname, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_channel_table[2].excl_sem, 1), 0, NULL }, + {DGAP_PORT_CUSTOM_PRNAME, "prname", 0600, NULL, NULL, NULL, &dgap_read_channel_custom_prname, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_channel_table[3].excl_sem, 1), 0, NULL }, + {DGAP_PORT_FEPSTATE, "fepstate", 0600, NULL, NULL, NULL, &dgap_read_channel_fepstate, NULL, + NULL, __SEMAPHORE_INITIALIZER(dgap_channel_table[4].excl_sem, 1), 0, NULL }, + {0} +}; + + +/* + * test_perm does NOT grant the superuser all rights automatically, because + * some entries are readonly even to root. + */ +static inline int test_perm(int mode, int op) +{ + if (!current_euid()) + mode >>= 6; + else if (in_egroup_p(0)) + mode >>= 3; + if ((mode & op & 0007) == op) + return 0; + if (capable(CAP_SYS_ADMIN)) + return 0; + return -EACCES; +} + + +/* + * /proc/sys support + */ +static inline int dgap_proc_match(int len, const char *name, struct proc_dir_entry *de) +{ + if (!de || !de->low_ino) + return 0; + if (de->namelen != len) + return 0; + return !memcmp(name, de->name, len); +} + + +/* + * Scan the entries in table and add them all to /proc at the position + * referred to by "root" + */ +static void dgap_register_proc_table(struct dgap_proc_entry *table, struct proc_dir_entry *root) +{ + struct proc_dir_entry *de; + int len; + mode_t mode; + + for (; table->magic; table++) { + /* Can't do anything without a proc name. */ + if (!table->name) { + DPR_PROC(("dgap_register_proc_table, no name...\n")); + continue; + } + + /* Maybe we can't do anything with it... */ + if (!table->read_handler && !table->write_handler && !table->child) { + DPR_PROC((KERN_WARNING "DGAP PROC: Can't register %s\n", table->name)); + continue; + } + + len = strlen(table->name); + mode = table->mode; + de = NULL; + + if (!table->child) { + mode |= S_IFREG; + } else { + mode |= S_IFDIR; + for (de = root->subdir; de; de = de->next) { + if (dgap_proc_match(len, table->name, de)) + break; + } + + /* If the subdir exists already, de is non-NULL */ + } + + if (!de) { + de = create_proc_entry(table->name, mode, root); + if (!de) + continue; + de->data = (void *) table; + if (!table->child) { + de->proc_iops = &dgap_proc_inode_ops; + de->proc_fops = &dgap_proc_file_ops; + } + } + + table->de = de; + + if (de->mode & S_IFDIR) + dgap_register_proc_table(table->child, de); + + } +} + + + +/* + * Unregister a /proc sysctl table and any subdirectories. + */ +static void dgap_unregister_proc_table(struct dgap_proc_entry *table, struct proc_dir_entry *root) +{ + struct proc_dir_entry *de; + + for (; table->magic; table++) { + if (!(de = table->de)) + continue; + + if (de->mode & S_IFDIR) { + if (!table->child) { + DPR_PROC((KERN_ALERT "Help - malformed sysctl tree on free\n")); + continue; + } + + /* recurse down into subdirectory... */ + DPR_PROC(("Recursing down a directory...\n")); + dgap_unregister_proc_table(table->child, de); + + /* Don't unregister directories which still have entries.. */ + if (de->subdir) + continue; + } + /* Don't unregister proc entries that are still being used.. */ + if ((atomic_read(&de->count)) != 1) { + DPR_PROC(("proc entry in use... Not removing...\n")); + continue; + } + dgap_remove_proc_entry(de); + table->de = NULL; + } +} + + + +static int dgap_gen_proc_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *de; + struct dgap_proc_entry *entry; + int (*handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos); + int ret = 0, error = 0; + + de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode); + if (!de || !de->data) { + ret = -ENXIO; + goto done; + } + + entry = (struct dgap_proc_entry *) de->data; + if (!entry) { + ret = -ENXIO; + goto done; + } + + down(&entry->excl_sem); + + if (entry->excl_cnt) { + ret = -EBUSY; + } else { + entry->excl_cnt++; + handler = entry->open_handler; + if (handler) { + error = (*handler) (entry, OUTBOUND, file, NULL, NULL, NULL); + if (error) { + entry->excl_cnt--; + ret = error; + } + } + } + + up(&entry->excl_sem); + +done: + + return ret; +} + + +static int dgap_gen_proc_close(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *de; + int (*handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos); + struct dgap_proc_entry *entry; + int error = 0; + + de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode); + if (!de || !de->data) + goto done; + + entry = (struct dgap_proc_entry *) de->data; + if (!entry) + goto done; + + down(&entry->excl_sem); + + if (entry->excl_cnt) + entry->excl_cnt = 0; + + + handler = entry->close_handler; + if (handler) { + error = (*handler) (entry, OUTBOUND, file, NULL, NULL, NULL); + } + + up(&entry->excl_sem); + +done: + return 0; +} + + +static ssize_t dgap_gen_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct proc_dir_entry *de; + struct dgap_proc_entry *entry; + int (*handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos2); + ssize_t res; + ssize_t error; + + de = (struct proc_dir_entry*) PDE(file->f_dentry->d_inode); + if (!de || !de->data) + return -ENXIO; + + entry = (struct dgap_proc_entry *) de->data; + if (!entry) + return -ENXIO; + + /* Test for read permission */ + if (test_perm(entry->mode, 4)) + return -EPERM; + + res = count; + + handler = entry->read_handler; + if (!handler) + return -ENXIO; + + error = (*handler) (entry, OUTBOUND, file, buf, &res, ppos); + if (error) + return error; + + return res; +} + + +static ssize_t dgap_gen_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct proc_dir_entry *de; + struct dgap_proc_entry *entry; + int (*handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + const char __user *buffer, ssize_t *lenp, loff_t *ppos2); + ssize_t res; + ssize_t error; + + de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode); + if (!de || !de->data) + return -ENXIO; + + entry = (struct dgap_proc_entry *) de->data; + if (!entry) + return -ENXIO; + + /* Test for write permission */ + if (test_perm(entry->mode, 2)) + return -EPERM; + + res = count; + + handler = entry->write_handler; + if (!handler) + return -ENXIO; + + error = (*handler) (entry, INBOUND, file, buf, &res, ppos); + if (error) + return error; + + return res; +} + + +static int dgap_proc_chk_perm(struct inode *inode, int op) +{ + return test_perm(inode->i_mode, op); +} + + +/* + * Return what is (hopefully) useful information about the + * driver. + */ +static int dgap_read_info(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + static int done = 0; + static char buf[4096]; + char *p = buf; + + DPR_PROC(("dgap_proc_info\n")); + + if (done) { + done = 0; + *lenp = 0; + return 0; + } + + p += sprintf(p, "Driver:\t\t%s\n", DG_NAME); + p += sprintf(p, "\n"); + p += sprintf(p, "Debug:\t\t0x%x\n", dgap_debug); + p += sprintf(p, "Rawreadok:\t0x%x\n", dgap_rawreadok); + p += sprintf(p, "Max Boards:\t%d\n", MAXBOARDS); + p += sprintf(p, "Total Boards:\t%d\n", dgap_NumBoards); + p += sprintf(p, "Poll rate:\t%dms\n", dgap_poll_tick); + p += sprintf(p, "Poll counter:\t%ld\n", dgap_poll_counter); + p += sprintf(p, "State:\t\t%s\n", dgap_driver_state_text[dgap_driver_state]); + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +/* + * When writing to the "info" entry point, I actually allow one + * to modify certain variables. This may be a sleazy overload + * of this /proc entry, but I don't want: + * + * a. to clutter /proc more than I have to + * b. to overload the "config" entry, which would be somewhat + * more natural + * c. necessarily advertise the fact this ability exists + * + * The continued support of this feature has not yet been + * guaranteed. + * + * Writing operates on a "state machine" principle. + * + * State 0: waiting for a symbol to start. Waiting for anything + * which isn't " ' = or whitespace. + * State 1: reading a symbol. If the character is a space, move + * to state 2. If =, move to state 3. If " or ', move + * to state 0. + * State 2: Waiting for =... suck whitespace. If anything other + * than whitespace, drop to state 0. + * State 3: Got =. Suck whitespace waiting for value to start. + * If " or ', go to state 4 (and remember which quote it + * was). Otherwise, go to state 5. + * State 4: Reading value, within quotes. Everything is added to + * value up until the matching quote. When you hit the + * matching quote, try to set the variable, then state 0. + * State 5: Reading value, outside quotes. Everything not " ' = + * or whitespace goes in value. Hitting one of the + * terminators tosses us back to state 0 after trying to + * set the variable. + */ +typedef enum { + INFO_NONE, INFO_INT, INFO_CHAR, INFO_SHORT, + INFO_LONG, INFO_PTR, INFO_STRING, INFO_END +} info_proc_var_val; + +static struct { + char *name; + info_proc_var_val type; + int rw; /* 0=readonly */ + void *val_ptr; +} dgap_info_vars[] = { + { "rawreadok", INFO_INT, 1, (void *) &dgap_rawreadok }, + { "pollrate", INFO_INT, 1, (void *) &dgap_poll_tick }, + { NULL, INFO_NONE, 0, NULL }, + { "debug", INFO_LONG, 1, (void *) &dgap_debug }, + { NULL, INFO_END, 0, NULL } +}; + +static void dgap_set_info_var(char *name, char *val) +{ + int i; + unsigned long newval; + unsigned char charval; + unsigned short shortval; + unsigned int intval; + + for (i = 0; dgap_info_vars[i].type != INFO_END; i++) { + if (dgap_info_vars[i].name) + if (!strcmp(name, dgap_info_vars[i].name)) + break; + } + + if (dgap_info_vars[i].type == INFO_END) + return; + if (dgap_info_vars[i].rw == 0) + return; + if (dgap_info_vars[i].val_ptr == NULL) + return; + + newval = simple_strtoul(val, NULL, 0 ); + + switch (dgap_info_vars[i].type) { + case INFO_CHAR: + charval = newval & 0xff; + APR(("Modifying %s (%lx) <= 0x%02x (%d)\n", + name, (long)(dgap_info_vars[i].val_ptr ), + charval, charval)); + *(uchar *)(dgap_info_vars[i].val_ptr) = charval; + break; + case INFO_SHORT: + shortval = newval & 0xffff; + APR(("Modifying %s (%lx) <= 0x%04x (%d)\n", + name, (long)(dgap_info_vars[i].val_ptr), + shortval, shortval)); + *(ushort *)(dgap_info_vars[i].val_ptr) = shortval; + break; + case INFO_INT: + intval = newval & 0xffffffff; + APR(("Modifying %s (%lx) <= 0x%08x (%d)\n", + name, (long)(dgap_info_vars[i].val_ptr), + intval, intval)); + *(uint *)(dgap_info_vars[i].val_ptr) = intval; + break; + case INFO_LONG: + APR(("Modifying %s (%lx) <= 0x%lx (%ld)\n", + name, (long)(dgap_info_vars[i].val_ptr), + newval, newval)); + *(ulong *)(dgap_info_vars[i].val_ptr) = newval; + break; + case INFO_PTR: + case INFO_STRING: + case INFO_END: + case INFO_NONE: + default: + break; + } +} + +static int dgap_write_info(struct dgap_proc_entry *table, int dir, struct file *filp, + const char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + static int state = 0; + #define MAXSYM 255 + static int sympos, valpos; + static char sym[MAXSYM + 1]; + static char val[MAXSYM + 1]; + static int quotchar = 0; + + int i; + + long len; + #define INBUFLEN 256 + char inbuf[INBUFLEN]; + + if (*ppos == 0) { + state = 0; + sympos = 0; sym[0] = 0; + valpos = 0; val[0] = 0; + quotchar = 0; + } + + if ((!*lenp) || (dir != INBOUND)) { + *lenp = 0; + return 0; + } + + len = *lenp; + + if (len > INBUFLEN - 1) + len = INBUFLEN - 1; + + if (copy_from_user(inbuf, buffer, len)) + return -EFAULT; + + inbuf[len] = 0; + + for (i = 0; i < len; i++) { + unsigned char c = inbuf[i]; + + switch (state) { + case 0: + quotchar = sympos = valpos = sym[0] = val[0] = 0; + if (!isspace(c) && (c != '\"') && + (c != '\'') && (c != '=')) { + sym[sympos++] = c; + state = 1; + break; + } + break; + case 1: + if (isspace(c)) { + sym[sympos] = 0; + state = 2; + break; + } + if (c == '=') { + sym[sympos] = 0; + state = 3; + break; + } + if ((c == '\"' ) || ( c == '\'' )) { + state = 0; + break; + } + if (sympos < MAXSYM) sym[sympos++] = c; + break; + case 2: + if (isspace(c)) break; + if (c == '=') { + state = 3; + break; + } + if ((c != '\"') && (c != '\'')) { + quotchar = sympos = valpos = sym[0] = val[0] = 0; + sym[sympos++] = c; + state = 1; + break; + } + state = 0; + break; + case 3: + if (isspace(c)) break; + if (c == '=') { + state = 0; + break; + } + if ((c == '\"') || (c == '\'')) { + state = 4; + quotchar = c; + break; + } + val[valpos++] = c; + state = 5; + break; + case 4: + if (c == quotchar) { + val[valpos] = 0; + dgap_set_info_var(sym, val); + state = 0; + break; + } + if (valpos < MAXSYM) val[valpos++] = c; + break; + case 5: + if (isspace(c) || (c == '\"') || + (c == '\'') || (c == '=')) { + val[valpos] = 0; + dgap_set_info_var(sym, val); + state = 0; + break; + } + if (valpos < MAXSYM) val[valpos++] = c; + break; + default: + break; + } + } + + *lenp = len; + *ppos += len; + + return len; +} + + +/* + * Return mknod information for the driver's devices. + */ +static int dgap_read_mknod(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + static int done = 0; + static char buf[4096]; + char *p = buf; + + DPR_PROC(("dgap_proc_info\n")); + + if (done) { + done = 0; + *lenp = 0; + return 0; + } + + DPR_PROC(("dgap_proc_mknod\n")); + + p += sprintf(p, "#\tCreate the management device.\n"); + p += sprintf(p, "/dev/dg/dgap/mgmt\t%d\t%d\t%d\t%d\n", DIGI_DGAP_MAJOR, MGMT_MGMT, 1, 0); + p += sprintf(p, "/dev/dg/dgap/downld\t%d\t%d\t%d\t%d\n", DIGI_DGAP_MAJOR, MGMT_DOWNLD, 1, 0); + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + + +/* + * Return what is (hopefully) useful information about the specific board. + */ +static int dgap_read_board_info(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct board_t *brd; + static int done = 0; + static char buf[4096]; + char *p = buf; + char *name; + + DPR_PROC(("dgap_proc_brd_info\n")); + + brd = (struct board_t *) table->data; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + name = brd->name; + + p += sprintf(p, "Board Name = %s\n", name); + p += sprintf(p, "Board Type = %d\n", brd->type); + p += sprintf(p, "Number of Ports = %d\n", brd->nasync); + + /* + * report some things about the PCI bus that are important + * to some applications + */ + p += sprintf(p, "Vendor ID = 0x%x\n", brd->vendor); + p += sprintf(p, "Device ID = 0x%x\n", brd->device); + p += sprintf(p, "Subvendor ID = 0x%x\n", brd->subvendor); + p += sprintf(p, "Subdevice ID = 0x%x\n", brd->subdevice); + p += sprintf(p, "Bus = %d\n", brd->pci_bus); + p += sprintf(p, "Slot = %d\n", brd->pci_slot); + + /* + * report the physical addresses assigned to us when we got + * registered + */ + p += sprintf(p, "Memory Base Address = 0x%lx\n", brd->membase); + p += sprintf(p, "Remapped Memory Base Address = 0x%p\n", brd->re_map_membase); + + p += sprintf(p, "Current state of board = %s\n", dgap_state_text[brd->state]); + + if (brd->bd_flags & BD_FEP5PLUS) + p += sprintf(p, "FEP5+ Support = Yes\n"); + else + p += sprintf(p, "FEP5+ Support = No\n"); + + p += sprintf(p, "Interrupt #: %ld. Times interrupted: %ld\n", + brd->irq, brd->intr_count); + p += sprintf(p, "Majors allocated to board = TTY: %d PR: %d\n", + brd->SerialDriver->major, brd->PrintDriver->major); + + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +static int dgap_read_board_vpddata(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct board_t *brd; + static int done = 0; + static char buf[4096]; + int i = 0, j = 0; + char *p = buf; + + DPR_PROC(("dgap_proc_brd_info\n")); + + brd = (struct board_t *) table->data; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + p += sprintf(p, "\n 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + + /* Ending at 0xC0, because nothing but zeros exist after that offset */ + for (i = 0; i < 0xC0; i++) { + j = i; + if (!(i % 16)) { + if (j > 0) { + p += sprintf(p, " "); + for (j = i - 16; j < i; j++) { + if (0x20 <= brd->vpd[j] && brd->vpd[j] <= 0x7e) + p += sprintf(p, "%c", brd->vpd[j]); + else + p += sprintf(p, "."); + } + p += sprintf(p, "\n"); + } + p += sprintf(p, "%04X ", i); + } + p += sprintf(p, "%02X ", brd->vpd[i]); + } + if (!(i % 16)) { + p += sprintf(p, " "); + for (j = i - 16; j < i; j++) { + if (0x20 <= brd->vpd[j] && brd->vpd[j] <= 0x7e) + p += sprintf(p, "%c", brd->vpd[j]); + else + p += sprintf(p, "."); + } + p += sprintf(p, "\n"); + } + + p += sprintf(p, "\n"); + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +static int dgap_read_board_vpd(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct board_t *brd; + static int done = 0; + static char buf[4096]; + char *p = buf; + long ptr; + u16 i, j; + uchar len; + + DPR_PROC(("dgap_proc_brd_info\n")); + + brd = (struct board_t *) table->data; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC) || !(brd->bd_flags & BD_HAS_VPD)) { + done = 0; + *lenp = 0; + return 0; + } + + ptr = 0; + + /* Description */ + if (brd->vpd[ptr] == 0x82) { + ptr++; + + p += sprintf(p, "*DS: "); + len = brd->vpd[ptr]; + ptr += 2; + for (i = len; i; i--) { + uchar a; + a = brd->vpd[ptr]; + ptr++; + p += sprintf(p, "%c", a); + } + p += sprintf(p, "\n"); + } + + /* VPD */ + if (brd->vpd[ptr] == 0x90) { + ptr += 3; + + i = brd->vpd[ptr]; + + while (i > 0) { + + uchar a, b; + uchar str1[16]; + + a = brd->vpd[ptr]; + ptr++; + b = brd->vpd[ptr]; + ptr++; + + if ((a == 'P') && (b == 'N')) { + strcpy (str1, "*PN"); + } + else if ((a == 'E') && (b == 'C')) { + strcpy (str1, "*EC"); + } + else if ((a == 'F') && (b == 'N')) { + strcpy (str1, "*FN"); + } + else if ((a == 'M') && (b == 'N')) { + strcpy (str1, "*MF"); + } + else if ((a == 'S') && (b == 'N')) { + strcpy (str1, "*SN"); + } + else if ((a == 'R') && (b == 'L')) { + strcpy (str1, "*RL"); + } + else if ((a == 'D') && (b == 'D')) { + strcpy (str1, "*DD"); + } + else if ((a == 'D') && (b == 'G')) { + strcpy (str1, "*DG"); + } + else if ((a == 'L') && (b == 'L')) { + strcpy (str1, "*LL"); + } + else { + break; + } + + len = brd->vpd[ptr]; + ptr++; + + p += sprintf(p, "%s: ", str1); + + for (j = len; j; j--) { + uchar aa; + aa = brd->vpd[ptr]; + ptr++; + p += sprintf(p, "%c", aa); + } + + p += sprintf(p, "\n"); + + i -= (3 + len); + } + } + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + + +/* + * Return what is (hopefully) useful stats about the specific board's ttys + */ +static int dgap_read_board_ttystats(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct board_t *brd; + static int done = 0; + static char buf[4096]; + char *p = buf; + int i = 0; + + DPR_PROC(("dgap_proc_brd_info\n")); + + brd = (struct board_t *) table->data; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + /* Prepare the Header Labels */ + p += sprintf(p, "%2s %10s %23s %10s\n", + "Ch", "Chars Rx", " Rx Par--Brk--Frm--Ovr", "Chars Tx"); + + for (i = 0; i < brd->nasync; i++) { + + struct channel_t *ch = brd->channels[i]; + p += sprintf(p, "%2d ", i); + p += sprintf(p, "%10ld ", ch->ch_rxcount); + p += sprintf(p, " %4ld %4ld %4ld %4ld ", ch->ch_err_parity, + ch->ch_err_break, ch->ch_err_frame, ch->ch_err_overrun); + p += sprintf(p, "%10ld ", ch->ch_txcount); + p += sprintf(p, "\n"); + } + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +/* + * Return what is (hopefully) useful flags about the specific board's ttys + */ +static int dgap_read_board_ttyflags(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct board_t *brd; + static int done = 0; + static char buf[4096]; + char *p = buf; + int i = 0; + + DPR_PROC(("dgap_proc_brd_info\n")); + + brd = (struct board_t *) table->data; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + /* Prepare the Header Labels */ + p += sprintf(p, "%2s %5s %5s %5s %5s %5s %10s Line Status Flags\n", + "Ch", "CFlag", "IFlag", "OFlag", "LFlag", "DFlag", "Baud"); + + for (i = 0; i < brd->nasync; i++) { + + struct channel_t *ch = brd->channels[i]; + + p += sprintf(p, "%2d ", i); + p += sprintf(p, "%5x ", ch->ch_c_cflag); + p += sprintf(p, "%5x ", ch->ch_c_iflag); + p += sprintf(p, "%5x ", ch->ch_c_oflag); + p += sprintf(p, "%5x ", ch->ch_c_lflag); + p += sprintf(p, "%5x ", ch->ch_digi.digi_flags); + p += sprintf(p, "%10d ", ch->ch_baud_info); + + if (!ch->ch_open_count) { + p += sprintf(p, " -- -- -- -- -- -- --") ; + } else { + p += sprintf(p, " op %s %s %s %s %s %s", + (ch->ch_mostat & UART_MCR_RTS) ? "rs" : "--", + (ch->ch_mistat & UART_MSR_CTS) ? "cs" : "--", + (ch->ch_mostat & UART_MCR_DTR) ? "tr" : "--", + (ch->ch_mistat & UART_MSR_DSR) ? "mr" : "--", + (ch->ch_mistat & UART_MSR_DCD) ? "cd" : "--", + (ch->ch_mistat & UART_MSR_RI) ? "ri" : "--"); + } + + p += sprintf(p, "\n"); + } + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +/* + * Return mknod information for the board's devices. + */ +static int dgap_read_board_mknod(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct board_t *brd; + static int done = 0; + static char buf[4096]; + char *p = buf; + + int bn; + struct cnode *cptr = NULL; + int found = FALSE; + int ncount = 0; + int starto = 0; + + + DPR_PROC(("dgap_proc_brd_info\n")); + + brd = (struct board_t *) table->data; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + bn = brd->boardnum; + + /* + * For each board, output the device information in + * a handy table format... + */ + p += sprintf(p, "# NAME\t\tMAJOR\tMINOR\tCOUNT\tSTART\n"); + + + for (cptr = brd->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; + } + + /* TTY devices */ + p += sprintf(p, "tty%s\t\t%d\t%d\t%d\t%d\n", + ptr1, brd->SerialDriver->major, + 0, dgap_config_get_number_of_ports(brd), starto); + + /* PR devices */ + p += sprintf(p, "pr%s\t\t%d\t%d\t%d\t%d\n", + ptr1, brd->PrintDriver->major, + 0, dgap_config_get_number_of_ports(brd), starto); + } + + if (cptr->type == CNODE) { + + /* TTY devices */ + p += sprintf(p, "tty%s\t\t%d\t%d\t%d\t%d\n", + cptr->u.conc.id, brd->SerialDriver->major, + ncount, cptr->u.conc.nport, + cptr->u.conc.v_start ? cptr->u.conc.start : 1); + + /* PR devices */ + p += sprintf(p, "pr%s\t\t%d\t%d\t%d\t%d\n", + cptr->u.conc.id, brd->PrintDriver->major, + ncount, cptr->u.conc.nport, + cptr->u.conc.v_start ? cptr->u.conc.start : 1); + + ncount += cptr->u.conc.nport; + } + + if (cptr->type == MNODE) { + + /* TTY devices */ + p += sprintf(p, "tty%s\t\t%d\t%d\t%d\t%d\n", + cptr->u.module.id, brd->SerialDriver->major, + ncount, cptr->u.module.nport, + cptr->u.module.v_start ? cptr->u.module.start : 1); + + /* PR devices */ + p += sprintf(p, "pr%s\t\t%d\t%d\t%d\t%d\n", + cptr->u.module.id, brd->PrintDriver->major, + ncount, cptr->u.module.nport, + cptr->u.module.v_start ? cptr->u.module.start : 1); + + ncount += cptr->u.module.nport; + + } + } + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + + + + +/* + * Return what is (hopefully) useful information about the specific channel. + */ +static int dgap_read_channel_info(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct channel_t *ch; + static int done = 0; + static char buf[4096]; + char *p = buf; + + DPR_PROC(("dgap_proc_info\n")); + + ch = (struct channel_t *) table->data; + + if (done || !ch || (ch->magic != DGAP_CHANNEL_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + p += sprintf(p, "Port number:\t\t%d\n", ch->ch_portnum); + p += sprintf(p, "\n"); + + /* Prepare the Header Labels */ + p += sprintf(p, "%10s %23s %10s\n", + "Chars Rx", " Rx Par--Brk--Frm--Ovr", "Chars Tx"); + p += sprintf(p, "%10ld ", ch->ch_rxcount); + p += sprintf(p, " %4ld %4ld %4ld %4ld ", ch->ch_err_parity, + ch->ch_err_break, ch->ch_err_frame, ch->ch_err_overrun); + p += sprintf(p, "%10ld ", ch->ch_txcount); + p += sprintf(p, "\n\n"); + + /* Prepare the Header Labels */ + p += sprintf(p, "%5s %5s %5s %5s %5s %10s Line Status Flags\n", + "CFlag", "IFlag", "OFlag", "LFlag", "DFlag", "Baud"); + + p += sprintf(p, "%5x ", ch->ch_c_cflag); + p += sprintf(p, "%5x ", ch->ch_c_iflag); + p += sprintf(p, "%5x ", ch->ch_c_oflag); + p += sprintf(p, "%5x ", ch->ch_c_lflag); + p += sprintf(p, "%5x ", ch->ch_digi.digi_flags); + p += sprintf(p, "%10d ", ch->ch_baud_info); + if (!ch->ch_open_count) { + p += sprintf(p, " -- -- -- -- -- -- --") ; + } else { + p += sprintf(p, " op %s %s %s %s %s %s", + (ch->ch_mostat & UART_MCR_RTS) ? "rs" : "--", + (ch->ch_mistat & UART_MSR_CTS) ? "cs" : "--", + (ch->ch_mostat & UART_MCR_DTR) ? "tr" : "--", + (ch->ch_mistat & UART_MSR_DSR) ? "mr" : "--", + (ch->ch_mistat & UART_MSR_DCD) ? "cd" : "--", + (ch->ch_mistat & UART_MSR_RI) ? "ri" : "--"); + } + p += sprintf(p, "\n\n"); + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +/* + * Return mknod information for the board's devices. + */ +static int dgap_read_channel_custom_ttyname(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct channel_t *ch; + struct board_t *brd; + static int done = 0; + static char buf[4096]; + char *p = buf; + + int cn; + int bn; + struct cnode *cptr = NULL; + int found = FALSE; + int ncount = 0; + int starto = 0; + int i = 0; + + DPR_PROC(("dgap_proc_brd_info\n")); + + ch = (struct channel_t *) table->data; + + if (done || !ch || (ch->magic != DGAP_CHANNEL_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + brd = ch->ch_bd; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + bn = brd->boardnum; + cn = ch->ch_portnum; + + /* + * For each board, output the device information in + * a handy table format... + */ + + for (cptr = brd->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_number_of_ports(brd); i++) { + if (cn == i) { + p += sprintf(p, "tty%s%02d\n", + ptr1, i + starto); + } + } + } + + if (cptr->type == CNODE) { + + for (i = 0; i < cptr->u.conc.nport; i++) { + if (cn == (i + ncount)) { + p += sprintf(p, "tty%s%02d\n", 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)) { + p += sprintf(p, "tty%s%02d\n", cptr->u.module.id, + i + (cptr->u.module.v_start ? cptr->u.module.start : 1)); + } + } + + ncount += cptr->u.module.nport; + + } + } + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +/* + * Return mknod information for the board's devices. + */ +static int dgap_read_channel_custom_prname(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct channel_t *ch; + struct board_t *brd; + static int done = 0; + static char buf[4096]; + char *p = buf; + + int cn; + int bn; + struct cnode *cptr = NULL; + int found = FALSE; + int ncount = 0; + int starto = 0; + int i = 0; + + DPR_PROC(("dgap_proc_brd_info\n")); + + ch = (struct channel_t *) table->data; + + if (done || !ch || (ch->magic != DGAP_CHANNEL_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + brd = ch->ch_bd; + + if (done || !brd || (brd->magic != DGAP_BOARD_MAGIC)) { + done = 0; + *lenp = 0; + return 0; + } + + bn = brd->boardnum; + cn = ch->ch_portnum; + + /* + * For each board, output the device information in + * a handy table format... + */ + + for (cptr = brd->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_number_of_ports(brd); i++) { + if (cn == i) { + p += sprintf(p, "pr%s%02d\n", + ptr1, i + starto); + } + } + } + + if (cptr->type == CNODE) { + + for (i = 0; i < cptr->u.conc.nport; i++) { + if (cn == (i + ncount)) { + p += sprintf(p, "pr%s%02d\n", 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)) { + p += sprintf(p, "pr%s%02d\n", cptr->u.module.id, + i + (cptr->u.module.v_start ? cptr->u.module.start : 1)); + } + } + + ncount += cptr->u.module.nport; + + } + } + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + + + + +static int dgap_open_channel_sniff(struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct channel_t *ch; + ulong lock_flags; + + ch = (struct channel_t *) table->data; + + if (!ch || (ch->magic != DGAP_CHANNEL_MAGIC)) + return 0; + + ch->ch_sniff_buf = dgap_driver_kzmalloc(SNIFF_MAX, GFP_KERNEL); + + DGAP_LOCK(ch->ch_lock, lock_flags); + ch->ch_sniff_flags |= SNIFF_OPEN; + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + return 0; +} + +static int dgap_close_channel_sniff(struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct channel_t *ch; + ulong lock_flags; + + ch = (struct channel_t *) table->data; + + if (!ch || (ch->magic != DGAP_CHANNEL_MAGIC)) + return 0; + + DGAP_LOCK(ch->ch_lock, lock_flags); + ch->ch_sniff_flags &= ~(SNIFF_OPEN); + kfree(ch->ch_sniff_buf); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + return 0; +} + + +/* + * Copy data from the monitoring buffer to the user, freeing space + * in the monitoring buffer for more messages + * + */ +static int dgap_read_channel_sniff(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct channel_t *ch; + int n; + int r; + int offset = 0; + int res = 0; + ssize_t rtn = 0; + ulong lock_flags; + + ch = (struct channel_t *) table->data; + + if (!ch || (ch->magic != DGAP_CHANNEL_MAGIC)) { + rtn = -ENXIO; + goto done; + } + + /* + * Wait for some data to appear in the buffer. + */ + DGAP_LOCK(ch->ch_lock, lock_flags); + + for (;;) { + n = (ch->ch_sniff_in - ch->ch_sniff_out) & SNIFF_MASK; + + if (n != 0) + break; + + ch->ch_sniff_flags |= SNIFF_WAIT_DATA; + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* + * Go to sleep waiting until the condition becomes true. + */ + rtn = wait_event_interruptible(ch->ch_sniff_wait, + ((ch->ch_sniff_flags & SNIFF_WAIT_DATA) == 0)); + + if (rtn) + goto done; + + DGAP_LOCK(ch->ch_lock, lock_flags); + } + + /* + * Read whatever is there. + */ + + if (n > *lenp) + n = *lenp; + + res = n; + + r = SNIFF_MAX - ch->ch_sniff_out; + + if (r <= n) { + DGAP_UNLOCK(ch->ch_lock, lock_flags); + rtn = copy_to_user(buffer, ch->ch_sniff_buf + ch->ch_sniff_out, r); + if (rtn) { + rtn = -EFAULT; + goto done; + } + + DGAP_LOCK(ch->ch_lock, lock_flags); + + ch->ch_sniff_out = 0; + n -= r; + offset = r; + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + rtn = copy_to_user(buffer + offset, ch->ch_sniff_buf + ch->ch_sniff_out, n); + if (rtn) { + rtn = -EFAULT; + goto done; + } + DGAP_LOCK(ch->ch_lock, lock_flags); + + ch->ch_sniff_out += n; + *ppos += res; + rtn = res; +// rtn = 0; + + /* + * Wakeup any thread waiting for buffer space. + */ + + if (ch->ch_sniff_flags & SNIFF_WAIT_SPACE) { + ch->ch_sniff_flags &= ~SNIFF_WAIT_SPACE; + wake_up_interruptible(&ch->ch_sniff_wait); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + +done: + return rtn; +} + + +static int dgap_read_channel_fepstate(struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos) +{ + struct channel_t *ch; + static int done = 0; + static char buf[4096]; + char *p = buf; + + DPR_PROC(("dgap_proc_info\n")); + + ch = (struct channel_t *) table->data; + + if (done || !ch || (ch->magic != DGAP_CHANNEL_MAGIC) || (ch->ch_bd->state != BOARD_READY)) { + done = 0; + *lenp = 0; + return 0; + } + + p += sprintf(p, "Port number: %d\n", ch->ch_portnum); + p += sprintf(p, "\n"); + + p += sprintf(p, "\tTPJMP:\t%x\n", ch->ch_bs->tp_jmp); + p += sprintf(p, "\tTCJMP:\t%x\n", ch->ch_bs->tc_jmp); + p += sprintf(p, "\tTPJMP:\t%x\n", ch->ch_bs->ri_jmp); + p += sprintf(p, "\tRPJMP:\t%x\n", ch->ch_bs->rp_jmp); + + p += sprintf(p, "\tTSEG:\t%x\n", ch->ch_bs->tx_seg); + p += sprintf(p, "\tTIN:\t%x\n", ch->ch_bs->tx_head); + p += sprintf(p, "\tTOUT:\t%x\n", ch->ch_bs->tx_tail); + p += sprintf(p, "\tTMAX:\t%x\n", ch->ch_bs->tx_max); + + p += sprintf(p, "\tRSEG:\t%x\n", ch->ch_bs->rx_seg); + p += sprintf(p, "\tRIN:\t%x\n", ch->ch_bs->rx_head); + p += sprintf(p, "\tROUT:\t%x\n", ch->ch_bs->rx_tail); + p += sprintf(p, "\tRMAX:\t%x\n", ch->ch_bs->rx_max); + + p += sprintf(p, "\tTLOW:\t%x\n", ch->ch_bs->tx_lw); + p += sprintf(p, "\tRLOW:\t%x\n", ch->ch_bs->rx_lw); + p += sprintf(p, "\tRHIGH:\t%x\n", ch->ch_bs->rx_hw); + p += sprintf(p, "\tINCR:\t%x\n", ch->ch_bs->incr); + + p += sprintf(p, "\tDEV:\t%x\n", ch->ch_bs->fepdev); + p += sprintf(p, "\tEDELAY:\t%x\n", ch->ch_bs->edelay); + p += sprintf(p, "\tBLEN:\t%x\n", ch->ch_bs->blen); + p += sprintf(p, "\tBTIME:\t%x\n", ch->ch_bs->btime); + + p += sprintf(p, "\tIFLAG:\t%x\n", ch->ch_bs->iflag); + p += sprintf(p, "\tOFLAG:\t%x\n", ch->ch_bs->oflag); + p += sprintf(p, "\tCFLAG:\t%x\n", ch->ch_bs->cflag); + + p += sprintf(p, "\tNUM:\t%x\n", ch->ch_bs->num); + p += sprintf(p, "\tRACT:\t%x\n", ch->ch_bs->ract); + p += sprintf(p, "\tBSTAT:\t%x\n", ch->ch_bs->bstat); + p += sprintf(p, "\tTBUSY:\t%x\n", ch->ch_bs->tbusy); + p += sprintf(p, "\tIEMPTY:\t%x\n", ch->ch_bs->iempty); + p += sprintf(p, "\tILOW:\t%x\n", ch->ch_bs->ilow); + p += sprintf(p, "\tIDATA:\t%x\n", ch->ch_bs->idata); + p += sprintf(p, "\tEFLAG:\t%x\n", ch->ch_bs->eflag); + + p += sprintf(p, "\tTFLAG:\t%x\n", ch->ch_bs->tflag); + p += sprintf(p, "\tRFLAG:\t%x\n", ch->ch_bs->rflag); + p += sprintf(p, "\tXMASK:\t%x\n", ch->ch_bs->xmask); + p += sprintf(p, "\tXVAL:\t%x\n", ch->ch_bs->xval); + p += sprintf(p, "\tMSTAT:\t%x\n", ch->ch_bs->m_stat); + p += sprintf(p, "\tEFMASK:\t%x\n", ch->ch_bs->m_change); + p += sprintf(p, "\tMINT:\t%x\n", ch->ch_bs->m_int); + p += sprintf(p, "\tLSTAT:\t%x\n", ch->ch_bs->m_last); + + p += sprintf(p, "\tMTRAN:\t%x\n", ch->ch_bs->mtran); + p += sprintf(p, "\tORUN:\t%x\n", ch->ch_bs->orun); + p += sprintf(p, "\tSTARTCA:%x\n", ch->ch_bs->astartc); + p += sprintf(p, "\tSTOPCA:\t%x\n", ch->ch_bs->astopc); + p += sprintf(p, "\tSTARTC\t%x\n", ch->ch_bs->startc); + p += sprintf(p, "\tSTOPC:\t%x\n", ch->ch_bs->stopc); + p += sprintf(p, "\tVNEXT:\t%x\n", ch->ch_bs->vnextc); + p += sprintf(p, "\tHFLOW:\t%x\n", ch->ch_bs->hflow); + p += sprintf(p, "\tFILLC:\t%x\n", ch->ch_bs->fillc); + p += sprintf(p, "\tOCHAR:\t%x\n", ch->ch_bs->ochar); + p += sprintf(p, "\tOMASK:\t%x\n", ch->ch_bs->omask); + + if (copy_to_user(buffer, buf, (p - (char *) buf))) + return -EFAULT; + + *lenp = p - (char *) buf; + *ppos += p - (char *) buf; + done = 1; + return 0; +} + + +/* + * Register the basic /proc/dgap files that appear whenever + * the driver is loaded. + */ +void dgap_proc_register_basic_prescan(void) +{ + int i; + + /* Initialize semaphores in each table slot */ + for (i = 0; i < DGAP_MAX_PROC_ENTRIES; i++) { + if (!dgap_table[i].magic) { + break; + } + init_MUTEX(&(dgap_table[i].excl_sem)); + } + + /* + * Register /proc/dgap + */ + ProcDGAP = proc_create("dgap", (0700 | S_IFDIR), NULL, &dgap_proc_file_ops); + dgap_register_proc_table(dgap_table, ProcDGAP); +} + + +/* + * Register the basic /proc/dgap files that appear whenever + * the driver is loaded. + */ +void dgap_proc_register_basic_postscan(int board_num) +{ + int i; + char board[10]; + sprintf(board, "%d", board_num); + + /* Set proc board entry pointer */ + dgap_Board[board_num]->proc_entry_pointer = create_proc_entry(board, (0700 | S_IFDIR), ProcDGAP); + + /* Create a new copy of the board_table... */ + dgap_Board[board_num]->dgap_board_table = dgap_driver_kzmalloc(sizeof(dgap_board_table), + GFP_KERNEL); + + /* Now copy the default table into that memory */ + memcpy(dgap_Board[board_num]->dgap_board_table, dgap_board_table, sizeof(dgap_board_table)); + + /* Initialize semaphores in each table slot */ + for (i = 0; i < DGAP_MAX_PROC_ENTRIES; i++) { + if (!dgap_Board[board_num]->dgap_board_table[i].magic) { + break; + } + + init_MUTEX(&(dgap_Board[board_num]->dgap_board_table[i].excl_sem)); + dgap_Board[board_num]->dgap_board_table[i].data = dgap_Board[board_num]; + + } + + /* Register board table into proc */ + dgap_register_proc_table(dgap_Board[board_num]->dgap_board_table, + dgap_Board[board_num]->proc_entry_pointer); +} + + +/* + * Register the channel /proc/dgap files that appear whenever + * the driver is loaded. + */ +void dgap_proc_register_channel_postscan(int board_num) +{ + int i, j; + + /* + * Add new entries for each port. + */ + for (i = 0; i < dgap_Board[board_num]->nasync; i++) { + + char channel[10]; + sprintf(channel, "%d", i); + + /* Set proc channel entry pointer */ + dgap_Board[board_num]->channels[i]->proc_entry_pointer = + create_proc_entry(channel, (0700 | S_IFDIR), + dgap_Board[board_num]->proc_entry_pointer); + + /* Create a new copy of the channel_table... */ + dgap_Board[board_num]->channels[i]->dgap_channel_table = + dgap_driver_kzmalloc(sizeof(dgap_channel_table), GFP_KERNEL); + + /* Now copy the default table into that memory */ + memcpy(dgap_Board[board_num]->channels[i]->dgap_channel_table, + dgap_channel_table, sizeof(dgap_channel_table)); + + /* Initialize semaphores in each table slot */ + for (j = 0; j < DGAP_MAX_PROC_ENTRIES; j++) { + if (!dgap_Board[board_num]->channels[i]->dgap_channel_table[j].magic) { + break; + } + + init_MUTEX(&(dgap_Board[board_num]->channels[i]->dgap_channel_table[j].excl_sem)); + dgap_Board[board_num]->channels[i]->dgap_channel_table[j].data = + dgap_Board[board_num]->channels[i]; + } + + /* Register channel table into proc */ + dgap_register_proc_table(dgap_Board[board_num]->channels[i]->dgap_channel_table, + dgap_Board[board_num]->channels[i]->proc_entry_pointer); + } +} + + +static void dgap_remove_proc_entry(struct proc_dir_entry *pde) +{ + if (!pde) { + DPR_PROC(("dgap_remove_proc_entry... NULL entry... not removing...\n")); + return; + } + + remove_proc_entry(pde->name, pde->parent); +} + + +void dgap_proc_unregister_all(void) +{ + int i = 0, j = 0; + + /* Walk each board, blowing away their proc entries... */ + for (i = 0; i < dgap_NumBoards; i++) { + + /* Walk each channel, blowing away their proc entries... */ + for (j = 0; j < dgap_Board[i]->nasync; j++) { + + if (dgap_Board[i]->channels[j]->dgap_channel_table) { + dgap_unregister_proc_table(dgap_Board[i]->channels[j]->dgap_channel_table, + dgap_Board[i]->channels[j]->proc_entry_pointer); + dgap_remove_proc_entry(dgap_Board[i]->channels[j]->proc_entry_pointer); + kfree(dgap_Board[i]->channels[j]->dgap_channel_table); + } + } + + if (dgap_Board[i]->dgap_board_table) { + dgap_unregister_proc_table(dgap_Board[i]->dgap_board_table, + dgap_Board[i]->proc_entry_pointer); + dgap_remove_proc_entry(dgap_Board[i]->proc_entry_pointer); + kfree(dgap_Board[i]->dgap_board_table); + } + } + + /* Blow away the top proc entry */ + dgap_unregister_proc_table(dgap_table, ProcDGAP); + dgap_remove_proc_entry(ProcDGAP); +} diff --git a/drivers/staging/dgap/dgap_proc.h b/drivers/staging/dgap/dgap_proc.h new file mode 100644 index 000000000000..9970d28816ee --- /dev/null +++ b/drivers/staging/dgap/dgap_proc.h @@ -0,0 +1,151 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + * + * $Id: dgap_proc.h,v 1.1 2009/10/23 14:01:57 markh Exp $ + * + * Description: + * + * Describes the private structures used to manipulate the "special" + * proc constructs (not read-only) used by the Digi Neo software. + * The concept is borrowed heavily from the "sysctl" interface of + * the kernel. I decided not to use the structures and functions + * provided by the kernel for two reasons: + * + * 1. Due to the planned use of "/proc" in the Neo driver, many + * of the functions of the "sysctl" interface would go unused. + * A simpler interface will be easier to maintain. + * + * 2. I'd rather divorce our "added package" from the kernel internals. + * If the "sysctl" structures should change, I will be insulated + * from those changes. These "/proc" entries won't be under the + * "sys" tree anyway, so there is no need to maintain a strict + * dependence relationship. + * + * Author: + * + * Scott H Kilau + * + */ + +#ifndef _DGAP_RW_PROC_H +#define _DGAP_RW_PROC_H + +/* + * The list of DGAP entries with r/w capabilities. + * These magic numbers are used for identification purposes. + */ +enum { + DGAP_INFO = 1, /* Get info about the running module */ + DGAP_MKNOD = 2, /* Get info about driver devices */ + DGAP_BOARD_INFO = 3, /* Get info about the specific board */ + DGAP_BOARD_VPD = 4, /* Get info about the board's VPD */ + DGAP_BOARD_VPDDATA = 5, /* Get info about the board's VPD */ + DGAP_BOARD_TTYSTATS = 6, /* Get info about the board's tty stats */ + DGAP_BOARD_TTYFLAGS = 7, /* Get info about the board's tty flags */ + DGAP_BOARD_MKNOD = 8, /* Get info about board devices */ + DGAP_PORT_INFO = 9, /* Get info about the specific port */ + DGAP_PORT_SNIFF = 10, /* Sniff data in/out of specific port */ + DGAP_PORT_CUSTOM_TTYNAME = 11, /* Get info about UDEV tty name */ + DGAP_PORT_CUSTOM_PRNAME = 12, /* Get info about UDEV pr name */ + DGAP_PORT_FEPSTATE = 13, /* Get info about Ports FEP state */ +}; + +#define DGAP_MAX_PROC_ENTRIES 999 + +/* + * Directions for proc handlers + */ +enum { + INBOUND = 1, /* Data being written to kernel */ + OUTBOUND = 2, /* Data being read from the kernel */ +}; + +/* + * Each entry in a DGAP proc directory is described with a + * "dgap_proc_entry" structure. A collection of these + * entries (in an array) represents the members associated + * with a particular "/proc" directory, and is referred to + * as a table. All "tables" are terminated by an entry with + * zeros for every member. + * + * The structure members are as follows: + * + * int magic -- ID number associated with this particular + * entry. Should be unique across all of + * DGAP. + * + * const char *name -- ASCII name associated with the /proc entry. + * + * mode_t mode -- File access permisssions for the /proc entry. + * + * dgap_proc_entry *child -- When set, this entry refers to a directory, + * and points to the table which describes the + * entries in the subdirectory + * + * dgap_proc_handler *open_handler -- When set, points to the fxn which + * does any "extra" open stuff. + * + * dgap_proc_handler *close_handler -- When set, points to the fxn which + * does any "extra" close stuff. + * + * dgap_proc_handler *read_handler -- When set, points to the fxn which + * handle outbound data flow + * + * dgap_proc_handler *write_handler -- When set, points to the fxn which + * handles inbound data flow + * + * struct proc_dir_entry *de -- Pointer to the directory entry for this + * object once registered. Used to grab + * the handle of the object for + * unregistration + * + * void *data; When set, points to the parent structure + * + */ + +struct dgap_proc_entry { + int magic; /* Integer identifier */ + const char *name; /* ASCII identifier */ + mode_t mode; /* File access permissions */ + struct dgap_proc_entry *child; /* Child pointer */ + + int (*open_handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos); + int (*close_handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + void *buffer, ssize_t *lenp, loff_t *ppos); + int (*read_handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + char __user *buffer, ssize_t *lenp, loff_t *ppos); + int (*write_handler) (struct dgap_proc_entry *table, int dir, struct file *filp, + const char __user *buffer, ssize_t *lenp, loff_t *ppos); + + struct proc_dir_entry *de; /* proc entry pointer */ + struct semaphore excl_sem; /* Protects exclusive access var */ + int excl_cnt; /* Counts number of curr accesses */ + void *data; /* Allows storing a pointer to parent */ +}; + + +void dgap_proc_register_basic_prescan(void); +void dgap_proc_unregister_all(void); +void dgap_proc_register_basic_postscan(int board_num); +void dgap_proc_register_channel_postscan(int board_num); + +#endif /* _DGAP_RW_PROC_H */ diff --git a/drivers/staging/dgap/dgap_sysfs.c b/drivers/staging/dgap/dgap_sysfs.c new file mode 100644 index 000000000000..6f98ac624830 --- /dev/null +++ b/drivers/staging/dgap/dgap_sysfs.c @@ -0,0 +1,796 @@ +/* + * Copyright 2004 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + * + * + * $Id: dgap_sysfs.c,v 1.1 2009/10/23 14:01:57 markh Exp $ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dgap_driver.h" +#include "dgap_proc.h" +#include "dgap_mgmt.h" +#include "dgap_conf.h" +#include "dgap_parse.h" + + +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_state_show(struct device_driver *ddp, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", dgap_driver_state_text[dgap_driver_state]); +} +static DRIVER_ATTR(state, S_IRUSR, dgap_driver_state_show, NULL); + + +static ssize_t dgap_driver_debug_show(struct device_driver *ddp, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%x\n", dgap_debug); +} + +static ssize_t dgap_driver_debug_store(struct device_driver *ddp, const char *buf, size_t count) +{ + sscanf(buf, "0x%x\n", &dgap_debug); + return count; +} +static DRIVER_ATTR(debug, (S_IRUSR | S_IWUSR), dgap_driver_debug_show, dgap_driver_debug_store); + + +static ssize_t dgap_driver_rawreadok_show(struct device_driver *ddp, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%x\n", dgap_rawreadok); +} + +static ssize_t dgap_driver_rawreadok_store(struct device_driver *ddp, const char *buf, size_t count) +{ + sscanf(buf, "0x%x\n", &dgap_rawreadok); + return count; +} +static DRIVER_ATTR(rawreadok, (S_IRUSR | S_IWUSR), dgap_driver_rawreadok_show, dgap_driver_rawreadok_store); + + +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) +{ + sscanf(buf, "%d\n", &dgap_poll_tick); + return count; +} +static DRIVER_ATTR(pollrate, (S_IRUSR | S_IWUSR), dgap_driver_pollrate_show, dgap_driver_pollrate_store); + + +void 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_debug); + rc |= driver_create_file(driverfs, &driver_attr_rawreadok); + rc |= driver_create_file(driverfs, &driver_attr_pollrate); + rc |= driver_create_file(driverfs, &driver_attr_pollcounter); + rc |= driver_create_file(driverfs, &driver_attr_state); + if (rc) { + printk(KERN_ERR "DGAP: sysfs driver_create_file failed!\n"); + } +} + + +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_debug); + driver_remove_file(driverfs, &driver_attr_rawreadok); + driver_remove_file(driverfs, &driver_attr_pollrate); + driver_remove_file(driverfs, &driver_attr_pollcounter); + driver_remove_file(driverfs, &driver_attr_state); +} + + +#define DGAP_VERIFY_BOARD(p, bd) \ + if (!p) \ + return (0); \ + \ + bd = dev_get_drvdata(p); \ + if (!bd || bd->magic != DGAP_BOARD_MAGIC) \ + return (0); \ + if (bd->state != BOARD_READY) \ + return (0); \ + + +static ssize_t dgap_ports_state_show(struct device *p, struct device_attribute *attr, char *buf) +{ + struct board_t *bd; + int count = 0; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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; + int i = 0; + + DGAP_VERIFY_BOARD(p, bd); + + 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); + + +/* this function creates the sys files that will export each signal status + * to sysfs each value will be put in a separate filename + */ +void dgap_create_ports_sysfiles(struct board_t *bd) +{ + int rc = 0; + + dev_set_drvdata(&bd->pdev->dev, bd); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_state); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_baud); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_msignals); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_iflag); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_cflag); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_oflag); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_lflag); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_rxcount); + rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_txcount); + if (rc) { + printk(KERN_ERR "DGAP: sysfs device_create_file failed!\n"); + } +} + + +/* removes all the sys files created for that port */ +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); +} + + +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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = (struct un_t *) 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 = NULL; + int found = FALSE; + int ncount = 0; + int starto = 0; + int i = 0; + + if (!d) + return (0); + un = (struct un_t *) 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_number_of_ports(bd); i++) { + if (cn == i) { + 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)) { + + return snprintf(buf, PAGE_SIZE, "%s%s%02d\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)) { + return snprintf(buf, PAGE_SIZE, "%s%s%02d\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 +}; + + +static struct attribute_group dgap_tty_attribute_group = { + .name = NULL, + .attrs = dgap_sysfs_tty_entries, +}; + + + + +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) { + printk(KERN_ERR "dgap: failed to create sysfs tty device attributes.\n"); + sysfs_remove_group(&c->kobj, &dgap_tty_attribute_group); + return; + } + + dev_set_drvdata(c, un); + +} + + +void dgap_remove_tty_sysfs(struct device *c) +{ + sysfs_remove_group(&c->kobj, &dgap_tty_attribute_group); +} diff --git a/drivers/staging/dgap/dgap_sysfs.h b/drivers/staging/dgap/dgap_sysfs.h new file mode 100644 index 000000000000..dde690eec5cf --- /dev/null +++ b/drivers/staging/dgap/dgap_sysfs.h @@ -0,0 +1,48 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_SYSFS_H +#define __DGAP_SYSFS_H + +#include "dgap_driver.h" + +#include + +struct board_t; +struct channel_t; +struct un_t; +struct pci_driver; +struct class_device; + +extern void dgap_create_ports_sysfiles(struct board_t *bd); +extern void dgap_remove_ports_sysfiles(struct board_t *bd); + +extern void dgap_create_driver_sysfiles(struct pci_driver *); +extern void dgap_remove_driver_sysfiles(struct pci_driver *); + +extern int dgap_tty_class_init(void); +extern int dgap_tty_class_destroy(void); + +extern void dgap_create_tty_sysfs(struct un_t *un, struct device *c); +extern void dgap_remove_tty_sysfs(struct device *c); + + +#endif diff --git a/drivers/staging/dgap/dgap_trace.c b/drivers/staging/dgap/dgap_trace.c new file mode 100644 index 000000000000..17610025b8f4 --- /dev/null +++ b/drivers/staging/dgap/dgap_trace.c @@ -0,0 +1,186 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + */ + +/* $Id: dgap_trace.c,v 1.1 2009/10/23 14:01:57 markh Exp $ */ + +#include +#include +#include /* For jiffies, task states */ +#include /* For tasklet and interrupt structs/defines */ +#include + +#include "dgap_driver.h" + +#define TRC_TO_CONSOLE 1 + +/* file level globals */ +static char *dgap_trcbuf; /* the ringbuffer */ + +#if defined(TRC_TO_KMEM) +static int dgap_trcbufi = 0; /* index of the tilde at the end of */ +#endif + +extern int dgap_trcbuf_size; /* size of the ringbuffer */ + +#if defined(TRC_TO_KMEM) +static DEFINE_SPINLOCK(dgap_tracef_lock); +#endif + +#if 0 + +#if !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) +void dgap_tracef(const char *fmt, ...) +{ + return; +} + +#else /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */ + +void dgap_tracef(const char *fmt, ...) +{ + va_list ap; + char buf[TRC_MAXMSG+1]; + size_t lenbuf; + int i; + static int failed = FALSE; +# if defined(TRC_TO_KMEM) + unsigned long flags; +#endif + + if(failed) + return; +# if defined(TRC_TO_KMEM) + DGAP_LOCK(dgap_tracef_lock, flags); +#endif + + /* Format buf using fmt and arguments contained in ap. */ + va_start(ap, fmt); + i = vsprintf(buf, fmt, ap); + va_end(ap); + lenbuf = strlen(buf); + +# if defined(TRC_TO_KMEM) + { + static int initd=0; + + /* + * Now, in addition to (or instead of) printing this stuff out + * (which is a buffered operation), also tuck it away into a + * corner of memory which can be examined post-crash in kdb. + */ + if (!initd) { + dgap_trcbuf = (char *) vmalloc(dgap_trcbuf_size); + if(!dgap_trcbuf) { + failed = TRUE; + printk("dgap: tracing init failed!\n"); + return; + } + + memset(dgap_trcbuf, '\0', dgap_trcbuf_size); + dgap_trcbufi = 0; + initd++; + + printk("dgap: tracing enabled - " TRC_DTRC + " 0x%lx 0x%x\n", + (unsigned long)dgap_trcbuf, + dgap_trcbuf_size); + } + +# if defined(TRC_ON_OVERFLOW_WRAP_AROUND) + /* + * This is the less CPU-intensive way to do things. We simply + * wrap around before we fall off the end of the buffer. A + * tilde (~) demarcates the current end of the trace. + * + * This method should be used if you are concerned about race + * conditions as it is less likely to affect the timing of + * things. + */ + + if (dgap_trcbufi + lenbuf >= dgap_trcbuf_size) { + /* We are wrapping, so wipe out the last tilde. */ + dgap_trcbuf[dgap_trcbufi] = '\0'; + /* put the new string at the beginning of the buffer */ + dgap_trcbufi = 0; + } + + strcpy(&dgap_trcbuf[dgap_trcbufi], buf); + dgap_trcbufi += lenbuf; + dgap_trcbuf[dgap_trcbufi] = '~'; + +# elif defined(TRC_ON_OVERFLOW_SHIFT_BUFFER) + /* + * This is the more CPU-intensive way to do things. If we + * venture into the last 1/8 of the buffer, we shift the + * last 7/8 of the buffer forward, wiping out the first 1/8. + * Advantage: No wrap-around, only truncation from the + * beginning. + * + * This method should not be used if you are concerned about + * timing changes affecting the behaviour of the driver (ie, + * race conditions). + */ + strcpy(&dgap_trcbuf[dgap_trcbufi], buf); + dgap_trcbufi += lenbuf; + dgap_trcbuf[dgap_trcbufi] = '~'; + dgap_trcbuf[dgap_trcbufi+1] = '\0'; + + /* If we're near the end of the trace buffer... */ + if (dgap_trcbufi > (dgap_trcbuf_size/8)*7) { + /* Wipe out the first eighth to make some more room. */ + strcpy(dgap_trcbuf, &dgap_trcbuf[dgap_trcbuf_size/8]); + dgap_trcbufi = strlen(dgap_trcbuf)-1; + /* Plop overflow message at the top of the buffer. */ + bcopy(TRC_OVERFLOW, dgap_trcbuf, strlen(TRC_OVERFLOW)); + } +# else +# error "TRC_ON_OVERFLOW_WRAP_AROUND or TRC_ON_OVERFLOW_SHIFT_BUFFER?" +# endif + } + DGAP_UNLOCK(dgap_tracef_lock, flags); + +# endif /* defined(TRC_TO_KMEM) */ +} + +#endif /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */ + +#endif + +/* + * dgap_tracer_free() + * + * + */ +void dgap_tracer_free(void) +{ + if(dgap_trcbuf) + vfree(dgap_trcbuf); +} diff --git a/drivers/staging/dgap/dgap_trace.h b/drivers/staging/dgap/dgap_trace.h new file mode 100644 index 000000000000..b21f46198e71 --- /dev/null +++ b/drivers/staging/dgap/dgap_trace.h @@ -0,0 +1,36 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + ***************************************************************************** + * Header file for dgap_trace.c + * + * $Id: dgap_trace.h,v 1.1 2009/10/23 14:01:57 markh Exp $ + */ + +#ifndef __DGAP_TRACE_H +#define __DGAP_TRACE_H + +#include "dgap_driver.h" + +void dgap_tracef(const char *fmt, ...); +void dgap_tracer_free(void); + +#endif + diff --git a/drivers/staging/dgap/dgap_tty.c b/drivers/staging/dgap/dgap_tty.c new file mode 100644 index 000000000000..e7aea5ef51c5 --- /dev/null +++ b/drivers/staging/dgap/dgap_tty.c @@ -0,0 +1,3651 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + */ + +/************************************************************************ + * + * This file implements the tty driver functionality for the + * FEP5 based product lines. + * + ************************************************************************ + * + * $Id: dgap_tty.c,v 1.3 2011/06/23 12:11:31 markh Exp $ + */ + +#include +#include +#include /* For jiffies, task states */ +#include /* For tasklet and interrupt structs/defines */ +#include +#include +#include +#include +#include +#include +#include /* For udelay */ +#include /* For copy_from_user/copy_to_user */ +#include /* For read[bwl]/write[bwl] */ +#include + +#include "dgap_driver.h" +#include "dgap_tty.h" +#include "dgap_types.h" +#include "dgap_fep5.h" +#include "dgap_parse.h" +#include "dgap_conf.h" +#include "dgap_sysfs.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) +#define init_MUTEX(sem) sema_init(sem, 1) +#define DECLARE_MUTEX(name) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) +#endif + +/* + * internal variables + */ +static struct board_t *dgap_BoardsByMajor[256]; +static uchar *dgap_TmpWriteBuf = NULL; +static DECLARE_MUTEX(dgap_TmpWriteSem); + +/* + * 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 DgapDefaultTermios = +{ + .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 function prototypes */ +static int dgap_tty_open(struct tty_struct *tty, struct file *file); +static void dgap_tty_close(struct tty_struct *tty, struct file *file); +static int dgap_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch); +static int dgap_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo); +static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info); +static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo); +static int dgap_tty_digisetedelay(struct tty_struct *tty, int __user *new_info); +static int dgap_tty_write_room(struct tty_struct* tty); +static int dgap_tty_chars_in_buffer(struct tty_struct* tty); +static void dgap_tty_start(struct tty_struct *tty); +static void dgap_tty_stop(struct tty_struct *tty); +static void dgap_tty_throttle(struct tty_struct *tty); +static void dgap_tty_unthrottle(struct tty_struct *tty); +static void dgap_tty_flush_chars(struct tty_struct *tty); +static void dgap_tty_flush_buffer(struct tty_struct *tty); +static void dgap_tty_hangup(struct tty_struct *tty); +static int dgap_wait_for_drain(struct tty_struct *tty); +static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value); +static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value); +static int dgap_tty_digisetcustombaud(struct tty_struct *tty, int __user *new_info); +static int dgap_tty_digigetcustombaud(struct tty_struct *tty, int __user *retinfo); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +static int dgap_tty_tiocmget(struct tty_struct *tty); +static int dgap_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +#else +static int dgap_tty_tiocmget(struct tty_struct *tty, struct file *file); +static int dgap_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); +#endif +static int dgap_tty_send_break(struct tty_struct *tty, int msec); +static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout); +static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf, int count); +static void dgap_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios); +static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c); +static void dgap_tty_send_xchar(struct tty_struct *tty, char ch); + +static 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_preinit() + * + * Initialize any global tty related data before we download any boards. + */ +int dgap_tty_preinit(void) +{ + unsigned long flags; + + DGAP_LOCK(dgap_global_lock, flags); + + /* + * Allocate a buffer for doing the copy from user space to + * kernel space in dgap_input(). We only use one buffer and + * control access to it with a semaphore. If we are paging, we + * are already in trouble so one buffer won't hurt much anyway. + */ + dgap_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_ATOMIC); + + if (!dgap_TmpWriteBuf) { + DGAP_UNLOCK(dgap_global_lock, flags); + DPR_INIT(("unable to allocate tmp write buf")); + return (-ENOMEM); + } + + DGAP_UNLOCK(dgap_global_lock, flags); + return(0); +} + + +/* + * dgap_tty_register() + * + * Init the tty subsystem for this board. + */ +int dgap_tty_register(struct board_t *brd) +{ + int rc = 0; + + DPR_INIT(("tty_register start")); + + brd->SerialDriver = alloc_tty_driver(MAXPORTS); + + snprintf(brd->SerialName, MAXTTYNAMELEN, "tty_dgap_%d_", brd->boardnum); + brd->SerialDriver->name = brd->SerialName; + brd->SerialDriver->name_base = 0; + brd->SerialDriver->major = 0; + brd->SerialDriver->minor_start = 0; + brd->SerialDriver->type = TTY_DRIVER_TYPE_SERIAL; + brd->SerialDriver->subtype = SERIAL_TYPE_NORMAL; + brd->SerialDriver->init_termios = DgapDefaultTermios; + brd->SerialDriver->driver_name = DRVSTR; + brd->SerialDriver->flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK); + + /* The kernel wants space to store pointers to tty_structs */ + brd->SerialDriver->ttys = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->SerialDriver->ttys) + return(-ENOMEM); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + brd->SerialDriver->refcount = brd->TtyRefCnt; +#endif + + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + tty_set_operations(brd->SerialDriver, &dgap_tty_ops); + + /* + * If we're doing transparent print, we have to do all of the above + * again, seperately so we don't get the LD confused about what major + * we are when we get into the dgap_tty_open() routine. + */ + brd->PrintDriver = alloc_tty_driver(MAXPORTS); + + snprintf(brd->PrintName, MAXTTYNAMELEN, "pr_dgap_%d_", brd->boardnum); + brd->PrintDriver->name = brd->PrintName; + brd->PrintDriver->name_base = 0; + brd->PrintDriver->major = 0; + brd->PrintDriver->minor_start = 0; + brd->PrintDriver->type = TTY_DRIVER_TYPE_SERIAL; + brd->PrintDriver->subtype = SERIAL_TYPE_NORMAL; + brd->PrintDriver->init_termios = DgapDefaultTermios; + brd->PrintDriver->driver_name = DRVSTR; + brd->PrintDriver->flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK); + + /* The kernel wants space to store pointers to tty_structs */ + brd->PrintDriver->ttys = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->PrintDriver->ttys) + return(-ENOMEM); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + brd->PrintDriver->refcount = brd->TtyRefCnt; +#endif + + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + tty_set_operations(brd->PrintDriver, &dgap_tty_ops); + + if (!brd->dgap_Major_Serial_Registered) { + /* Register tty devices */ + rc = tty_register_driver(brd->SerialDriver); + if (rc < 0) { + APR(("Can't register tty device (%d)\n", rc)); + return(rc); + } + brd->dgap_Major_Serial_Registered = TRUE; + dgap_BoardsByMajor[brd->SerialDriver->major] = brd; + brd->dgap_Serial_Major = brd->SerialDriver->major; + } + + if (!brd->dgap_Major_TransparentPrint_Registered) { + /* Register Transparent Print devices */ + rc = tty_register_driver(brd->PrintDriver); + if (rc < 0) { + APR(("Can't register Transparent Print device (%d)\n", rc)); + return(rc); + } + brd->dgap_Major_TransparentPrint_Registered = TRUE; + dgap_BoardsByMajor[brd->PrintDriver->major] = brd; + brd->dgap_TransparentPrint_Major = brd->PrintDriver->major; + } + + DPR_INIT(("DGAP REGISTER TTY: MAJORS: %d %d\n", brd->SerialDriver->major, + brd->PrintDriver->major)); + + return (rc); +} + + +/* + * dgap_tty_init() + * + * Init the tty subsystem. Called once per board after board has been + * downloaded and init'ed. + */ +int dgap_tty_init(struct board_t *brd) +{ + int i; + int tlw; + uint true_count = 0; + uchar *vaddr; + uchar modem = 0; + struct channel_t *ch; + struct bs_t *bs; + struct cm_t *cm; + + if (!brd) + return (-ENXIO); + + DPR_INIT(("dgap_tty_init start\n")); + + /* + * Initialize board structure elements. + */ + + vaddr = brd->re_map_membase; + true_count = readw((vaddr + NCHAN)); + + brd->nasync = dgap_config_get_number_of_ports(brd); + + if (!brd->nasync) { + brd->nasync = brd->maxports; + } + + if (brd->nasync > brd->maxports) { + brd->nasync = brd->maxports; + } + + if (true_count != brd->nasync) { + if ((brd->type == PPCM) && (true_count == 64)) { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", + brd->name, brd->nasync, true_count)); + } + else if ((brd->type == PPCM) && (true_count == 0)) { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", + brd->name, brd->nasync, true_count)); + } + else { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\n", + brd->name, brd->nasync, true_count)); + } + + 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(-ENXIO); + } + } + + /* + * Allocate channel memory that might not have been allocated + * when the driver was first loaded. + */ + for (i = 0; i < brd->nasync; i++) { + if (!brd->channels[i]) { + brd->channels[i] = dgap_driver_kzmalloc(sizeof(struct channel_t), GFP_ATOMIC); + if (!brd->channels[i]) { + DPR_CORE(("%s:%d Unable to allocate memory for channel struct\n", + __FILE__, __LINE__)); + } + } + } + + ch = brd->channels[0]; + vaddr = brd->re_map_membase; + + bs = (struct bs_t *) ((ulong) vaddr + CHANBUF); + cm = (struct cm_t *) ((ulong) vaddr + CMDBUF); + + brd->bd_bs = bs; + + /* Set up channel variables */ + for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) { + + if (!brd->channels[i]) + continue; + + DGAP_SPINLOCK_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 + ((ch->ch_bs->tx_seg) << 4); + ch->ch_raddr = vaddr + ((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; + + /* .25 second delay */ + ch->ch_close_delay = 250; + + /* + * Set queue water marks, interrupt mask, + * and general tty parameters. + */ + ch->ch_tlw = tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : ch->ch_tsize / 2; + + 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); + init_waitqueue_head(&ch->ch_sniff_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)); + } + + + DPR_INIT(("dgap_tty_init finish\n")); + + return (0); +} + + +/* + * dgap_tty_post_uninit() + * + * UnInitialize any global tty related data. + */ +void dgap_tty_post_uninit(void) +{ + if (dgap_TmpWriteBuf) { + kfree(dgap_TmpWriteBuf); + dgap_TmpWriteBuf = NULL; + } +} + + +/* + * dgap_tty_uninit() + * + * Uninitialize the TTY portion of this driver. Free all memory and + * resources. + */ +void dgap_tty_uninit(struct board_t *brd) +{ + int i = 0; + + if (brd->dgap_Major_Serial_Registered) { + dgap_BoardsByMajor[brd->SerialDriver->major] = NULL; + brd->dgap_Serial_Major = 0; + for (i = 0; i < brd->nasync; i++) { + dgap_remove_tty_sysfs(brd->channels[i]->ch_tun.un_sysfs); + tty_unregister_device(brd->SerialDriver, i); + } + tty_unregister_driver(brd->SerialDriver); + if (brd->SerialDriver->ttys) { + kfree(brd->SerialDriver->ttys); + brd->SerialDriver->ttys = NULL; + } + put_tty_driver(brd->SerialDriver); + brd->dgap_Major_Serial_Registered = FALSE; + } + + if (brd->dgap_Major_TransparentPrint_Registered) { + dgap_BoardsByMajor[brd->PrintDriver->major] = NULL; + brd->dgap_TransparentPrint_Major = 0; + for (i = 0; i < brd->nasync; i++) { + dgap_remove_tty_sysfs(brd->channels[i]->ch_pun.un_sysfs); + tty_unregister_device(brd->PrintDriver, i); + } + tty_unregister_driver(brd->PrintDriver); + if (brd->PrintDriver->ttys) { + kfree(brd->PrintDriver->ttys); + brd->PrintDriver->ttys = NULL; + } + put_tty_driver(brd->PrintDriver); + brd->dgap_Major_TransparentPrint_Registered = FALSE; + } +} + + +#define TMPBUFLEN (1024) + +/* + * dgap_sniff - Dump data out to the "sniff" buffer if the + * proc sniff file is opened... + */ +static void dgap_sniff_nowait_nolock(struct channel_t *ch, uchar *text, uchar *buf, int len) +{ + struct timeval tv; + int n; + int r; + int nbuf; + int i; + int tmpbuflen; + char tmpbuf[TMPBUFLEN]; + char *p = tmpbuf; + int too_much_data; + + /* Leave if sniff not open */ + if (!(ch->ch_sniff_flags & SNIFF_OPEN)) + return; + + do_gettimeofday(&tv); + + /* Create our header for data dump */ + p += sprintf(p, "<%ld %ld><%s><", tv.tv_sec, tv.tv_usec, text); + tmpbuflen = p - tmpbuf; + + do { + too_much_data = 0; + + for (i = 0; i < len && tmpbuflen < (TMPBUFLEN - 4); i++) { + p += sprintf(p, "%02x ", *buf); + buf++; + tmpbuflen = p - tmpbuf; + } + + if (tmpbuflen < (TMPBUFLEN - 4)) { + if (i > 0) + p += sprintf(p - 1, "%s\n", ">"); + else + p += sprintf(p, "%s\n", ">"); + } else { + too_much_data = 1; + len -= i; + } + + nbuf = strlen(tmpbuf); + p = tmpbuf; + + /* + * Loop while data remains. + */ + while (nbuf > 0 && ch->ch_sniff_buf != 0) { + /* + * Determine the amount of available space left in the + * buffer. If there's none, wait until some appears. + */ + n = (ch->ch_sniff_out - ch->ch_sniff_in - 1) & SNIFF_MASK; + + /* + * If there is no space left to write to in our sniff buffer, + * we have no choice but to drop the data. + * We *cannot* sleep here waiting for space, because this + * function was probably called by the interrupt/timer routines! + */ + if (n == 0) { + return; + } + + /* + * Copy as much data as will fit. + */ + + if (n > nbuf) + n = nbuf; + + r = SNIFF_MAX - ch->ch_sniff_in; + + if (r <= n) { + memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, r); + + n -= r; + ch->ch_sniff_in = 0; + p += r; + nbuf -= r; + } + + memcpy(ch->ch_sniff_buf + ch->ch_sniff_in, p, n); + + ch->ch_sniff_in += n; + p += n; + nbuf -= n; + + /* + * Wakeup any thread waiting for data + */ + if (ch->ch_sniff_flags & SNIFF_WAIT_DATA) { + ch->ch_sniff_flags &= ~SNIFF_WAIT_DATA; + wake_up_interruptible(&ch->ch_sniff_wait); + } + } + + /* + * If the user sent us too much data to push into our tmpbuf, + * we need to keep looping around on all the data. + */ + if (too_much_data) { + p = tmpbuf; + tmpbuflen = 0; + } + + } while (too_much_data); +} + + +/*======================================================================= + * + * dgap_input - Process received data. + * + * ch - Pointer to channel structure. + * + *=======================================================================*/ + +void dgap_input(struct channel_t *ch) +{ + struct board_t *bd; + struct bs_t *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 = 0; + int n = 0; + uchar *buf; + uchar tmpchar; + int s = 0; + + 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; + + DPR_READ(("dgap_input start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("No data on port %d\n", ch->ch_portnum)); + 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) || !(tp->termios->c_cflag & CREAD) || + (ch->ch_tun.un_flags & UN_CLOSING)) { + + DPR_READ(("input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum)); + DPR_READ(("input. tp: %p tp->magic: %x MAGIC:%x ch flags: %x\n", + tp, tp ? tp->magic : 0, TTY_MAGIC, ch->ch_tun.un_flags)); + writew(head, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(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)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("Port %d throttled, not reading any data. head: %x tail: %x\n", + ch->ch_portnum, head, tail)); + return; + } + + /* + * Ignore oruns. + */ + tmpchar = readb(&(bs->orun)); + if (tmpchar) { + ch->ch_err_overrun++; + writeb(0, &(bs->orun)); + } + + DPR_READ(("dgap_input start 2\n")); + + /* Decide how much data we can send into the tty layer */ + if (dgap_rawreadok && tp->real_raw) + flip_len = MYFLIPLEN; + else + 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) - tp->read_cnt); + + 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)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("dgap_input 1 - finish\n")); + 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, (char *) ch->ch_raddr + tail, s); + dgap_sniff_nowait_nolock(ch, "USER READ", buf, 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 (dgap_rawreadok && tp->real_raw) { + + /* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_READ(("dgap_input. %d real_raw len:%d calling receive_buf for buffer for board %d\n", + __LINE__, len, ch->ch_bd->boardnum)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + tp->ldisc->ops->receive_buf(tp, ch->ch_bd->flipbuf, NULL, len); +#else + tp->ldisc.ops->receive_buf(tp, ch->ch_bd->flipbuf, NULL, len); +#endif + } + else { + 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, len); + tty_insert_flip_string_flags(tp, ch->ch_bd->flipbuf, + ch->ch_bd->flipflagbuf, len); + } + else { + len = tty_buffer_request_room(tp, len); + tty_insert_flip_string(tp, ch->ch_bd->flipbuf, len); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + /* Tell the tty layer its okay to "eat" the data now */ + tty_flip_buffer_push(tp); + } + + if (ld) + tty_ldisc_deref(ld); + + DPR_READ(("dgap_input - finish\n")); +} + + +/************************************************************************ + * Determines when CARRIER changes state and takes appropriate + * action. + ************************************************************************/ +void dgap_carrier(struct channel_t *ch) +{ + struct board_t *bd; + + int virt_carrier = 0; + int phys_carrier = 0; + + DPR_CARR(("dgap_carrier called...\n")); + + 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)) { + DPR_CARR(("mistat: %x D_CD: %x\n", 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; + } + + + DPR_CARR(("DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier)); + + /* + * 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. + */ + + DPR_CARR(("carrier: virt DCD rose\n")); + + 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. + */ + + DPR_CARR(("carrier: physical DCD rose\n")); + + 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) { + DPR_CARR(("Sending tty hangup\n")); + tty_hangup(ch->ch_tun.un_tty); + } + + if (ch->ch_pun.un_open_count > 0) { + DPR_CARR(("Sending pr hangup\n")); + 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; +} + + +/************************************************************************ + * + * 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 *bs; + uint major = 0; + uint minor = 0; + int rc = 0; + ulong lock_flags; + ulong lock_flags2; + u16 head; + + rc = 0; + + major = MAJOR(tty_devnum(tty)); + minor = MINOR(tty_devnum(tty)); + + if (major > 255) { + return -ENXIO; + } + + /* Get board pointer from our array of majors we have allocated */ + brd = dgap_BoardsByMajor[major]; + if (!brd) { + return -ENXIO; + } + + /* + * 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; + } + + DGAP_LOCK(brd->bd_lock, lock_flags); + + /* The wait above should guarentee this cannot happen */ + if (brd->state != BOARD_READY) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return -ENXIO; + } + + /* If opened device is greater than our number of ports, bail. */ + if (MINOR(tty_devnum(tty)) > brd->nasync) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return -ENXIO; + } + + ch = brd->channels[minor]; + if (!ch) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return -ENXIO; + } + + /* Grab channel lock */ + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* Figure out our type */ + if (major == brd->dgap_Serial_Major) { + un = &brd->channels[minor]->ch_tun; + un->un_type = DGAP_SERIAL; + } + else if (major == brd->dgap_TransparentPrint_Major) { + un = &brd->channels[minor]->ch_pun; + un->un_type = DGAP_PRINT; + } + else { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DPR_OPEN(("%d Unknown TYPE!\n", __LINE__)); + return -ENXIO; + } + + /* Store our unit into driver_data, so we always have it available. */ + tty->driver_data = un; + + DPR_OPEN(("Open called. MAJOR: %d MINOR:%d unit: %p NAME: %s\n", + MAJOR(tty_devnum(tty)), MINOR(tty_devnum(tty)), un, brd->name)); + + /* + * Error if channel info pointer is 0. + */ + if ((bs = ch->ch_bs) == 0) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DPR_OPEN(("%d BS is 0!\n", __LINE__)); + return -ENXIO; + } + + DPR_OPEN(("%d: tflag=%x pflag=%x\n", __LINE__, ch->ch_tun.un_flags, ch->ch_pun.un_flags)); + + /* + * 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)) { + + DPR_OPEN(("dgap_open: initializing channel in open...\n")); + + 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(tty); + + /* + * follow protocol for opening port + */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + rc = dgap_block_til_ready(tty, file, ch); + + if (!un->un_tty) { + return -ENODEV; + } + + if (rc) { + DPR_OPEN(("dgap_tty_open returning after dgap_block_til_ready " + "with %d\n", rc)); + } + + /* No going back now, increment our unit and channel counters */ + DGAP_LOCK(ch->ch_lock, lock_flags); + ch->ch_open_count++; + un->un_open_count++; + un->un_flags |= (UN_ISOPEN); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("dgap_tty_open finished\n")); + return (rc); +} + + +/* + * 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 = NULL; + ulong lock_flags; + uint old_flags = 0; + int sleep_on_un_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC || !file || !ch || ch->magic != DGAP_CHANNEL_MAGIC) { + return (-ENXIO); + } + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) { + return (-ENXIO); + } + + DPR_OPEN(("dgap_block_til_ready - before block.\n")); + + DGAP_LOCK(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 = -ENXIO; + 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) { + DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); + break; + } + + if (ch->ch_flags & CH_FCAR) { + DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); + 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)) { + DPR_OPEN(("%d: signal pending...\n", __LINE__)); + retval = -ERESTARTSYS; + break; + } + + DPR_OPEN(("dgap_block_til_ready - blocking.\n")); + + /* + * 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. + */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("Going to sleep on %s flags...\n", + (sleep_on_un_flags ? "un" : "ch"))); + + /* + * 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)); + } + + DPR_OPEN(("After sleep... retval: %x\n", retval)); + + /* + * We got woken up for some reason. + * Before looping around, grab our channel lock. + */ + DGAP_LOCK(ch->ch_lock, lock_flags); + } + + ch->ch_wopen--; + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("dgap_block_til_ready - after blocking.\n")); + + if (retval) { + DPR_OPEN(("dgap_block_til_ready - done. error. retval: %x\n", retval)); + return(retval); + } + + DPR_OPEN(("dgap_block_til_ready - done no error. jiffies: %lu\n", jiffies)); + + return(0); +} + + +/* + * 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; + + DPR_CLOSE(("dgap_hangup called. ch->ch_open_count: %d un->un_open_count: %d\n", + ch->ch_open_count, un->un_open_count)); + + /* flush the transmit queues */ + dgap_tty_flush_buffer(tty); + + DPR_CLOSE(("dgap_hangup finished. ch->ch_open_count: %d un->un_open_count: %d\n", + ch->ch_open_count, un->un_open_count)); +} + + + +/* + * dgap_tty_close() + * + */ +static void dgap_tty_close(struct tty_struct *tty, struct file *file) +{ + struct ktermios *ts; + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags; + int rc = 0; + + 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; + + ts = tty->termios; + + DPR_CLOSE(("Close called\n")); + + DGAP_LOCK(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. + */ + APR(("tty->count is 1, un open count is %d\n", un->un_open_count)); + un->un_open_count = 1; + } + + if (--un->un_open_count < 0) { + APR(("bad serial port open count of %d\n", un->un_open_count)); + un->un_open_count = 0; + } + + ch->ch_open_count--; + + if (ch->ch_open_count && un->un_open_count) { + DPR_CLOSE(("dgap_tty_close: not last close ch: %d un:%d\n", + ch->ch_open_count, un->un_open_count)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + return; + } + + /* OK, its the last close on the unit */ + DPR_CLOSE(("dgap_tty_close - last close on unit procedures\n")); + + 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); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* wait for output to drain */ + /* This will also return if we take an interrupt */ + + DPR_CLOSE(("Calling wait_for_drain\n")); + rc = dgap_wait_for_drain(tty); + DPR_CLOSE(("After calling wait_for_drain\n")); + + if (rc) { + DPR_BASIC(("dgap_tty_close - bad return: %d ", rc)); + } + + dgap_tty_flush_buffer(tty); + tty_ldisc_flush(tty); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + tty->closing = 0; + + /* + * If we have HUPCL set, lower DTR and RTS + */ + if (ch->ch_c_cflag & HUPCL ) { + DPR_CLOSE(("Close. HUPCL set, dropping DTR/RTS\n")); + 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. + */ + if (ch->ch_close_delay) { + DPR_CLOSE(("Close. Sleeping for RTS/DTR drop\n")); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + dgap_ms_sleep(ch->ch_close_delay); + DGAP_LOCK(ch->ch_lock, lock_flags); + + DPR_CLOSE(("Close. After sleeping for RTS/DTR drop\n")); + } + } + + 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; + + DPR_CLOSE(("Close. Doing wakeups\n")); + wake_up_interruptible(&ch->ch_flags_wait); + wake_up_interruptible(&un->un_flags_wait); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_BASIC(("dgap_tty_close - complete\n")); +} + + +/* + * 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 = NULL; + struct channel_t *ch = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + uchar tbusy; + uint chars = 0; + u16 thead, ttail, tmask, chead, ctail; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (tty == NULL) + 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); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(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) { + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + } + chars = 1; + } + } + + DPR_WRITE(("dgap_tty_chars_in_buffer. Port: %x - %d (head: %d tail: %d tsize: %d)\n", + ch->ch_portnum, chars, thead, ttail, ch->ch_tsize)); + return(chars); +} + + +static int dgap_wait_for_drain(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + struct bs_t *bs; + int ret = -EIO; + uint count = 1; + ulong lock_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bs = ch->ch_bs; + if (!bs) + return ret; + + ret = 0; + + DPR_DRAIN(("dgap_wait_for_drain start\n")); + + /* Loop until data is drained */ + while (count != 0) { + + count = dgap_tty_chars_in_buffer(tty); + + if (count == 0) + break; + + /* Set flag waiting for drain */ + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + DGAP_UNLOCK(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; + } + } + + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags &= ~(UN_EMPTY); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_DRAIN(("dgap_wait_for_drain finish\n")); + 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 tty_struct *tty, int bytes_available) +{ + struct channel_t *ch = NULL; + struct un_t *un = NULL; + + if (tty == NULL) + return (bytes_available); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (bytes_available); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (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 = NULL; + struct bs_t *bs = NULL; + + 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 = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + u16 head, tail, tmask; + int ret = 0; + ulong lock_flags = 0; + + if (tty == NULL || dgap_TmpWriteBuf == NULL) + 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); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + tmask = ch->ch_tsize - 1; + head = readw(&(bs->tx_head)) & tmask; + tail = readw(&(bs->tx_tail)) & tmask; + + if ((ret = tail - head - 1) < 0) + ret += ch->ch_tsize; + + /* Limit printer to maxcps */ + ret = dgap_maxcps_room(tty, 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); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_WRITE(("dgap_tty_write_room - %d tail: %d head: %d\n", ret, tail, head)); + + return(ret); +} + + +/* + * 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. + */ + DPR_WRITE(("dgap_tty_put_char called\n")); + dgap_tty_write(tty, &c, 1); + return 1; +} + + +/* + * 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 = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + char *vaddr = NULL; + u16 head, tail, tmask, remain; + int bufcount = 0, n = 0; + int orig_count = 0; + ulong lock_flags; + int from_user = 0; + + if (tty == NULL || dgap_TmpWriteBuf == NULL) + 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); + + DPR_WRITE(("dgap_tty_write: Port: %x tty=%p user=%d len=%d\n", + ch->ch_portnum, tty, from_user, count)); + + /* + * Store original amount of characters passed in. + * This helps to figure out if we should ask the FEP + * to send us an event when it has more space available. + */ + orig_count = count; + + DGAP_LOCK(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; + + if ((bufcount = tail - head - 1) < 0) + bufcount += ch->ch_tsize; + + DPR_WRITE(("%d: bufcount: %x count: %x tail: %x head: %x tmask: %x\n", + __LINE__, bufcount, count, tail, head, tmask)); + + /* + * Limit printer output to maxcps overall, with bursts allowed + * up to bufsize characters. + */ + bufcount = dgap_maxcps_room(tty, 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); + DGAP_UNLOCK(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; + } + + /* + * If there is nothing left to copy, or I can't handle any more data, leave. + */ + if (count <= 0) { + dgap_set_firmware_event(un, UN_LOW | UN_EMPTY); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + return(0); + } + + if (from_user) { + + count = min(count, WRITEBUFLEN); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* + * If data is coming from user space, copy it into a temporary + * buffer so we don't get swapped out while doing the copy to + * the board. + */ + /* we're allowed to block if it's from_user */ + if (down_interruptible(&dgap_TmpWriteSem)) { + return (-EINTR); + } + + if (copy_from_user(dgap_TmpWriteBuf, (const uchar __user *) buf, count)) { + up(&dgap_TmpWriteSem); + printk("Write: Copy from user failed!\n"); + return -EFAULT; + } + + DGAP_LOCK(ch->ch_lock, lock_flags); + + buf = dgap_TmpWriteBuf; + } + + 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, (uchar *) buf, remain); + dgap_sniff_nowait_nolock(ch, "USER WRITE", (uchar *) 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, (uchar *) buf, remain); + dgap_sniff_nowait_nolock(ch, "USER WRITE", (uchar *) 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; + } + + if (from_user) { + DGAP_UNLOCK(ch->ch_lock, lock_flags); + up(&dgap_TmpWriteSem); + } + else { + DGAP_UNLOCK(ch->ch_lock, lock_flags); + } + + DPR_WRITE(("Write finished - Write %d bytes of %d.\n", count, orig_count)); + + return (count); +} + + + +/* + * Return modem signals to ld. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +static int dgap_tty_tiocmget(struct tty_struct *tty) +#else +static int dgap_tty_tiocmget(struct tty_struct *tty, struct file *file) +#endif +{ + struct channel_t *ch; + struct un_t *un; + int result = -EIO; + uchar mstat = 0; + ulong lock_flags; + + if (!tty || tty->magic != TTY_MAGIC) + return result; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return result; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return result; + + DPR_IOCTL(("dgap_tty_tiocmget start\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + mstat = readb(&(ch->ch_bs->m_stat)); + /* Append any outbound signals that might be pending... */ + mstat |= ch->ch_mostat; + + DGAP_UNLOCK(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; + + DPR_IOCTL(("dgap_tty_tiocmget finish\n")); + + return result; +} + + +/* + * dgap_tty_tiocmset() + * + * Set modem signals, called by ld. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +static int dgap_tty_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +#else +static int dgap_tty_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +#endif +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int ret = -EIO; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + DPR_IOCTL(("dgap_tty_tiocmset start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_tiocmset finish\n")); + + 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; + int ret = -EIO; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + switch (msec) { + case -1: + msec = 0xFFFF; + break; + case 0: + msec = 1; + break; + default: + msec /= 10; + break; + } + + DPR_IOCTL(("dgap_tty_send_break start 1. %lx\n", jiffies)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); +#if 0 + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); +#endif + dgap_cmdw(ch, SBREAK, (u16) msec, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_send_break finish\n")); + + 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) +{ + int rc; + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return; + } + return; +} + + + +/* + * 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; + + DPR_IOCTL(("dgap_tty_send_xchar start 1. %lx\n", jiffies)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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 + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_send_xchar finish\n")); + + return; +} + + + + +/* + * Return modem signals to ld. + */ +static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value) +{ + int result = 0; + uchar mstat = 0; + ulong lock_flags; + int rc = 0; + + DPR_IOCTL(("dgap_get_modem_info start\n")); + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return(-ENXIO); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + mstat = readb(&(ch->ch_bs->m_stat)); + /* Append any outbound signals that might be pending... */ + mstat |= ch->ch_mostat; + + DGAP_UNLOCK(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; + + rc = put_user(result, value); + + DPR_IOCTL(("dgap_get_modem_info finish\n")); + return(rc); +} + + +/* + * dgap_set_modem_info() + * + * Set modem signals, called by ld. + */ +static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int ret = -ENXIO; + unsigned int arg = 0; + ulong lock_flags; + ulong lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + DPR_IOCTL(("dgap_set_modem_info() start\n")); + + ret = get_user(arg, value); + if (ret) { + DPR_IOCTL(("dgap_set_modem_info %d ret: %x. finished.\n", __LINE__, ret)); + return(ret); + } + + DPR_IOCTL(("dgap_set_modem_info: command: %x arg: %x\n", command, arg)); + + 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); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_set_modem_info finish\n")); + + return (0); +} + + +/* + * dgap_tty_digigeta() + * + * Ioctl to get the information for ditty. + * + * + * + */ +static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo) +{ + struct channel_t *ch; + struct un_t *un; + struct digi_t 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)); + + DGAP_LOCK(ch->ch_lock, lock_flags); + memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); + DGAP_UNLOCK(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 tty_struct *tty, struct digi_t __user *new_info) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + struct digi_t new_digi; + ulong lock_flags = 0; + unsigned long lock_flags2; + + DPR_IOCTL(("DIGI_SETA start\n")); + + 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); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-EFAULT); + + if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) { + DPR_IOCTL(("DIGI_SETA failed copy_from_user\n")); + return(-EFAULT); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("DIGI_SETA finish\n")); + + 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)); + + DGAP_LOCK(ch->ch_lock, lock_flags); + tmp = readw(&(ch->ch_bs->edelay)); + DGAP_UNLOCK(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 tty_struct *tty, int __user *new_info) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int new_digi; + ulong lock_flags; + ulong lock_flags2; + + DPR_IOCTL(("DIGI_SETA start\n")); + + 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); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-EFAULT); + + if (copy_from_user(&new_digi, new_info, sizeof(int))) { + DPR_IOCTL(("DIGI_SETEDELAY failed copy_from_user\n")); + return(-EFAULT); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + writew((u16) new_digi, &(ch->ch_bs->edelay)); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("DIGI_SETA finish\n")); + + return(0); +} + + +/* + * dgap_tty_digigetcustombaud() + * + * Ioctl to get the current custom baud rate setting. + */ +static int dgap_tty_digigetcustombaud(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)); + + DGAP_LOCK(ch->ch_lock, lock_flags); + tmp = dgap_get_custom_baud(ch); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_IOCTL(("DIGI_GETCUSTOMBAUD. Returning %d\n", tmp)); + + 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 tty_struct *tty, int __user *new_info) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + uint new_rate; + ulong lock_flags; + ulong lock_flags2; + + DPR_IOCTL(("DIGI_SETCUSTOMBAUD start\n")); + + 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); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-EFAULT); + + + if (copy_from_user(&new_rate, new_info, sizeof(unsigned int))) { + DPR_IOCTL(("DIGI_SETCUSTOMBAUD failed copy_from_user\n")); + return(-EFAULT); + } + + if (bd->bd_flags & BD_FEP5PLUS) { + + DPR_IOCTL(("DIGI_SETCUSTOMBAUD. Setting %d\n", new_rate)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_custom_speed = new_rate; + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + } + + DPR_IOCTL(("DIGI_SETCUSTOMBAUD finish\n")); + + 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; + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(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; + + DPR_IOCTL(("dgap_tty_throttle start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_flags |= (CH_RXBLOCK); +#if 1 + dgap_cmdw(ch, RPAUSE, 0, 0); +#endif + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_throttle finish\n")); +} + + +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; + + DPR_IOCTL(("dgap_tty_unthrottle start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_flags &= ~(CH_RXBLOCK); + +#if 1 + dgap_cmdw(ch, RRESUME, 0, 0); +#endif + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_unthrottle finish\n")); +} + + +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; + + DPR_IOCTL(("dgap_tty_start start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, RESUMETX, 0, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_start finish\n")); +} + + +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; + + DPR_IOCTL(("dgap_tty_stop start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, PAUSETX, 0, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_stop finish\n")); +} + + +/* + * 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; + + DPR_IOCTL(("dgap_tty_flush_chars start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* TODO: Do something here */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_flush_chars finish\n")); +} + + + +/* + * 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 = 0; + + 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; + + DPR_IOCTL(("dgap_tty_flush_buffer on port: %d start\n", ch->ch_portnum)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(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); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); + + DPR_IOCTL(("dgap_tty_flush_buffer finish\n")); +} + + + +/***************************************************************************** + * + * 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, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int rc; + u16 head = 0; + 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); + + DPR_IOCTL(("dgap_tty_ioctl start on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if (un->un_open_count <= 0) { + DPR_BASIC(("dgap_tty_ioctl - unit not open.\n")); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(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); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if(((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) { + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + 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); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + 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); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + 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. + */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return 0; + + case TIOCGSOFTCAR: + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg); + return(rc); + + case TIOCSSOFTCAR: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + rc = get_user(arg, (unsigned long __user *) arg); + if (rc) + return(rc); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); + dgap_param(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + return(0); + + case TIOCMGET: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_get_modem_info(ch, uarg)); + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_set_modem_info(tty, 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) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(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)) { + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->tx_head)); + dgap_cmdw(ch, FLUSHTX, (u16) head, 0 ); + dgap_cmdw(ch, RESUMETX, 0, 0); + if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + if (waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + + /* Can't hold any locks when calling tty_wakeup! */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + tty_wakeup(tty); + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + + /* pretend we didn't recognize this IOCTL */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n", + __LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return(-ENOIOCTLCMD); + +#ifdef TIOCGETP + case TIOCGETP: +#endif + case TCGETS: + case TCGETA: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + if (tty->ldisc->ops->ioctl) { +#else + if (tty->ldisc.ops->ioctl) { +#endif + int retval = (-ENXIO); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + if (tty->termios) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) + retval = ((tty->ldisc->ops->ioctl) (tty, file, cmd, arg)); +#else + retval = ((tty->ldisc.ops->ioctl) (tty, file, cmd, arg)); +#endif + } + + DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n", + __LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + return(retval); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n", + __LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + 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 */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + /* pretend we didn't recognize this */ + return(-ENOIOCTLCMD); + + case TCSETAW: + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", 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) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(rc); + } + + DPR_IOCTL(("dgap_ioctl - in TCXONC - %d\n", cmd)); + switch (arg) { + + case TCOON: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_tty_start(tty); + return(0); + case TCOOFF: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_tty_stop(tty); + return(0); + case TCION: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + /* Make the ld do it */ + return(-ENOIOCTLCMD); + case TCIOFF: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + /* Make the ld do it */ + return(-ENOIOCTLCMD); + default: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-EINVAL); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-ENOIOCTLCMD); + + case DIGI_GETA: + /* get information for ditty */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digigeta(tty, uarg)); + + case DIGI_SETAW: + case DIGI_SETAF: + + /* set information for ditty */ + if (cmd == (DIGI_SETAW)) { + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + else { + tty_ldisc_flush(tty); + } + /* fall thru */ + + case DIGI_SETA: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digiseta(tty, uarg)); + + case DIGI_GEDELAY: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digigetedelay(tty, uarg)); + + case DIGI_SEDELAY: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digisetedelay(tty, uarg)); + + case DIGI_GETCUSTOMBAUD: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digigetcustombaud(tty, uarg)); + + case DIGI_SETCUSTOMBAUD: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digisetcustombaud(tty, uarg)); + + case DIGI_RESET_PORT: + dgap_firmware_reset_port(ch); + dgap_param(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return 0; + + default: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl - in default\n")); + DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n", + dgap_ioctl_name(cmd), cmd, arg)); + + return(-ENOIOCTLCMD); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n", + dgap_ioctl_name(cmd), cmd, arg)); + + return(0); +} diff --git a/drivers/staging/dgap/dgap_tty.h b/drivers/staging/dgap/dgap_tty.h new file mode 100644 index 000000000000..464a460b6be8 --- /dev/null +++ b/drivers/staging/dgap/dgap_tty.h @@ -0,0 +1,39 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_TTY_H +#define __DGAP_TTY_H + +#include "dgap_driver.h" + +int dgap_tty_register(struct board_t *brd); + +int dgap_tty_preinit(void); +int dgap_tty_init(struct board_t *); + +void dgap_tty_post_uninit(void); +void dgap_tty_uninit(struct board_t *); + +void dgap_carrier(struct channel_t *ch); +void dgap_input(struct channel_t *ch); + + +#endif diff --git a/drivers/staging/dgap/dgap_types.h b/drivers/staging/dgap/dgap_types.h new file mode 100644 index 000000000000..eca38c7f359d --- /dev/null +++ b/drivers/staging/dgap/dgap_types.h @@ -0,0 +1,36 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_TYPES_H +#define __DGAP_TYPES_H + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +/* Required for our shared headers! */ +typedef unsigned char uchar; + +#endif diff --git a/drivers/staging/dgap/digi.h b/drivers/staging/dgap/digi.h new file mode 100644 index 000000000000..651e2e5e93c7 --- /dev/null +++ b/drivers/staging/dgap/digi.h @@ -0,0 +1,376 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: digi.h,v 1.1 2009/10/23 14:01:57 markh Exp $ + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DIGI_H +#define __DIGI_H + +/************************************************************************ + *** Definitions for Digi ditty(1) command. + ************************************************************************/ + + +/* + * Copyright (c) 1988-96 Digi International Inc., All Rights Reserved. + */ + +/************************************************************************ + * This module provides application access to special Digi + * serial line enhancements which are not standard UNIX(tm) features. + ************************************************************************/ + +#if !defined(TIOCMODG) + +#define TIOCMODG ('d'<<8) | 250 /* get modem ctrl state */ +#define TIOCMODS ('d'<<8) | 251 /* set modem ctrl state */ + +#ifndef TIOCM_LE +#define TIOCM_LE 0x01 /* line enable */ +#define TIOCM_DTR 0x02 /* data terminal ready */ +#define TIOCM_RTS 0x04 /* request to send */ +#define TIOCM_ST 0x08 /* secondary transmit */ +#define TIOCM_SR 0x10 /* secondary receive */ +#define TIOCM_CTS 0x20 /* clear to send */ +#define TIOCM_CAR 0x40 /* carrier detect */ +#define TIOCM_RNG 0x80 /* ring indicator */ +#define TIOCM_DSR 0x100 /* data set ready */ +#define TIOCM_RI TIOCM_RNG /* ring (alternate) */ +#define TIOCM_CD TIOCM_CAR /* carrier detect (alt) */ +#endif + +#endif + +#if !defined(TIOCMSET) +#define TIOCMSET ('d'<<8) | 252 /* set modem ctrl state */ +#define TIOCMGET ('d'<<8) | 253 /* set modem ctrl state */ +#endif + +#if !defined(TIOCMBIC) +#define TIOCMBIC ('d'<<8) | 254 /* set modem ctrl state */ +#define TIOCMBIS ('d'<<8) | 255 /* set modem ctrl state */ +#endif + + +#if !defined(TIOCSDTR) +#define TIOCSDTR ('e'<<8) | 0 /* set DTR */ +#define TIOCCDTR ('e'<<8) | 1 /* clear DTR */ +#endif + +/************************************************************************ + * Ioctl command arguments for DIGI parameters. + ************************************************************************/ +#define DIGI_GETA ('e'<<8) | 94 /* Read params */ + +#define DIGI_SETA ('e'<<8) | 95 /* Set params */ +#define DIGI_SETAW ('e'<<8) | 96 /* Drain & set params */ +#define DIGI_SETAF ('e'<<8) | 97 /* Drain, flush & set params */ + +#define DIGI_KME ('e'<<8) | 98 /* Read/Write Host */ + /* Adapter Memory */ + +#define DIGI_GETFLOW ('e'<<8) | 99 /* Get startc/stopc flow */ + /* control characters */ +#define DIGI_SETFLOW ('e'<<8) | 100 /* Set startc/stopc flow */ + /* control characters */ +#define DIGI_GETAFLOW ('e'<<8) | 101 /* Get Aux. startc/stopc */ + /* flow control chars */ +#define DIGI_SETAFLOW ('e'<<8) | 102 /* Set Aux. startc/stopc */ + /* flow control chars */ + +#define DIGI_GEDELAY ('d'<<8) | 246 /* Get edelay */ +#define DIGI_SEDELAY ('d'<<8) | 247 /* Set edelay */ + +struct digiflow_t { + unsigned char startc; /* flow cntl start char */ + unsigned char stopc; /* flow cntl stop char */ +}; + + +#ifdef FLOW_2200 +#define F2200_GETA ('e'<<8) | 104 /* Get 2x36 flow cntl flags */ +#define F2200_SETAW ('e'<<8) | 105 /* Set 2x36 flow cntl flags */ +#define F2200_MASK 0x03 /* 2200 flow cntl bit mask */ +#define FCNTL_2200 0x01 /* 2x36 terminal flow cntl */ +#define PCNTL_2200 0x02 /* 2x36 printer flow cntl */ +#define F2200_XON 0xf8 +#define P2200_XON 0xf9 +#define F2200_XOFF 0xfa +#define P2200_XOFF 0xfb + +#define FXOFF_MASK 0x03 /* 2200 flow status mask */ +#define RCVD_FXOFF 0x01 /* 2x36 Terminal XOFF rcvd */ +#define RCVD_PXOFF 0x02 /* 2x36 Printer XOFF rcvd */ +#endif + +/************************************************************************ + * Values for digi_flags + ************************************************************************/ +#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */ +#define DIGI_FAST 0x0002 /* Fast baud rates */ +#define RTSPACE 0x0004 /* RTS input flow control */ +#define CTSPACE 0x0008 /* CTS output flow control */ +#define DSRPACE 0x0010 /* DSR output flow control */ +#define DCDPACE 0x0020 /* DCD output flow control */ +#define DTRPACE 0x0040 /* DTR input flow control */ +#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */ +#define DIGI_FORCEDCD 0x0100 /* Force carrier */ +#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */ +#define DIGI_AIXON 0x0400 /* Aux flow control in fep */ +#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl*/ +#define DIGI_PP_INPUT 0x1000 /* Change parallel port to input*/ +#define DIGI_DTR_TOGGLE 0x2000 /* Support DTR Toggle */ +#define DIGI_422 0x4000 /* for 422/232 selectable panel */ +#define DIGI_RTS_TOGGLE 0x8000 /* Support RTS Toggle */ + +/************************************************************************ + * These options are not supported on the comxi. + ************************************************************************/ +#define DIGI_COMXI (DIGI_FAST|DIGI_COOK|DSRPACE|DCDPACE|DTRPACE) + +#define DIGI_PLEN 28 /* String length */ +#define DIGI_TSIZ 10 /* Terminal string len */ + +/************************************************************************ + * Structure used with ioctl commands for DIGI parameters. + ************************************************************************/ +struct digi_t { + unsigned short digi_flags; /* Flags (see above) */ + unsigned short digi_maxcps; /* Max printer CPS */ + unsigned short digi_maxchar; /* Max chars in print queue */ + unsigned short digi_bufsize; /* Buffer size */ + unsigned char digi_onlen; /* Length of ON string */ + unsigned char digi_offlen; /* Length of OFF string */ + char digi_onstr[DIGI_PLEN]; /* Printer on string */ + char digi_offstr[DIGI_PLEN]; /* Printer off string */ + char digi_term[DIGI_TSIZ]; /* terminal string */ +}; + +/************************************************************************ + * KME definitions and structures. + ************************************************************************/ +#define RW_IDLE 0 /* Operation complete */ +#define RW_READ 1 /* Read Concentrator Memory */ +#define RW_WRITE 2 /* Write Concentrator Memory */ + +struct rw_t { + unsigned char rw_req; /* Request type */ + unsigned char rw_board; /* Host Adapter board number */ + unsigned char rw_conc; /* Concentrator number */ + unsigned char rw_reserved; /* Reserved for expansion */ + unsigned long rw_addr; /* Address in concentrator */ + unsigned short rw_size; /* Read/write request length */ + unsigned char rw_data[128]; /* Data to read/write */ +}; + +/*********************************************************************** + * Shrink Buffer and Board Information definitions and structures. + + ************************************************************************/ + /* Board type return codes */ +#define PCXI_TYPE 1 /* Board type at the designated port is a PC/Xi */ +#define PCXM_TYPE 2 /* Board type at the designated port is a PC/Xm */ +#define PCXE_TYPE 3 /* Board type at the designated port is a PC/Xe */ +#define MCXI_TYPE 4 /* Board type at the designated port is a MC/Xi */ +#define COMXI_TYPE 5 /* Board type at the designated port is a COM/Xi */ + + /* Non-Zero Result codes. */ +#define RESULT_NOBDFND 1 /* A Digi product at that port is not config installed */ +#define RESULT_NODESCT 2 /* A memory descriptor was not obtainable */ +#define RESULT_NOOSSIG 3 /* FEP/OS signature was not detected on the board */ +#define RESULT_TOOSML 4 /* Too small an area to shrink. */ +#define RESULT_NOCHAN 5 /* Channel structure for the board was not found */ + +struct shrink_buf_struct { + unsigned long shrink_buf_vaddr; /* Virtual address of board */ + unsigned long shrink_buf_phys; /* Physical address of board */ + unsigned long shrink_buf_bseg; /* Amount of board memory */ + unsigned long shrink_buf_hseg; /* '186 Begining of Dual-Port */ + + unsigned long shrink_buf_lseg; /* '186 Begining of freed memory */ + unsigned long shrink_buf_mseg; /* Linear address from start of + dual-port were freed memory + begins, host viewpoint. */ + + unsigned long shrink_buf_bdparam; /* Parameter for xxmemon and + xxmemoff */ + + unsigned long shrink_buf_reserva; /* Reserved */ + unsigned long shrink_buf_reservb; /* Reserved */ + unsigned long shrink_buf_reservc; /* Reserved */ + unsigned long shrink_buf_reservd; /* Reserved */ + + unsigned char shrink_buf_result; /* Reason for call failing + Zero is Good return */ + unsigned char shrink_buf_init; /* Non-Zero if it caused an + xxinit call. */ + + unsigned char shrink_buf_anports; /* Number of async ports */ + unsigned char shrink_buf_snports; /* Number of sync ports */ + unsigned char shrink_buf_type; /* Board type 1 = PC/Xi, + 2 = PC/Xm, + 3 = PC/Xe + 4 = MC/Xi + 5 = COMX/i */ + unsigned char shrink_buf_card; /* Card number */ + +}; + +/************************************************************************ + * Structure to get driver status information + ************************************************************************/ +struct digi_dinfo { + unsigned long dinfo_nboards; /* # boards configured */ + char dinfo_reserved[12]; /* for future expansion */ + char dinfo_version[16]; /* driver version */ +}; + +#define DIGI_GETDD ('d'<<8) | 248 /* get driver info */ + +/************************************************************************ + * Structure used with ioctl commands for per-board information + * + * physsize and memsize differ when board has "windowed" memory + ************************************************************************/ +struct digi_info { + unsigned long info_bdnum; /* Board number (0 based) */ + unsigned long info_ioport; /* io port address */ + unsigned long info_physaddr; /* memory address */ + unsigned long info_physsize; /* Size of host mem window */ + unsigned long info_memsize; /* Amount of dual-port mem */ + /* on board */ + unsigned short info_bdtype; /* Board type */ + unsigned short info_nports; /* number of ports */ + char info_bdstate; /* board state */ + char info_reserved[7]; /* for future expansion */ +}; + +#define DIGI_GETBD ('d'<<8) | 249 /* get board info */ + +struct digi_stat { + unsigned int info_chan; /* Channel number (0 based) */ + unsigned int info_brd; /* Board number (0 based) */ + unsigned long info_cflag; /* cflag for channel */ + unsigned long info_iflag; /* iflag for channel */ + unsigned long info_oflag; /* oflag for channel */ + unsigned long info_mstat; /* mstat for channel */ + unsigned long info_tx_data; /* tx_data for channel */ + unsigned long info_rx_data; /* rx_data for channel */ + unsigned long info_hflow; /* hflow for channel */ + unsigned long info_reserved[8]; /* for future expansion */ +}; + +#define DIGI_GETSTAT ('d'<<8) | 244 /* get board info */ +/************************************************************************ + * + * Structure used with ioctl commands for per-channel information + * + ************************************************************************/ +struct digi_ch { + unsigned long info_bdnum; /* Board number (0 based) */ + unsigned long info_channel; /* Channel index number */ + unsigned long info_ch_cflag; /* Channel cflag */ + unsigned long info_ch_iflag; /* Channel iflag */ + unsigned long info_ch_oflag; /* Channel oflag */ + unsigned long info_chsize; /* Channel structure size */ + unsigned long info_sleep_stat; /* sleep status */ + dev_t info_dev; /* device number */ + unsigned char info_initstate; /* Channel init state */ + unsigned char info_running; /* Channel running state */ + long reserved[8]; /* reserved for future use */ +}; + +/* +* This structure is used with the DIGI_FEPCMD ioctl to +* tell the driver which port to send the command for. +*/ +struct digi_cmd { + int cmd; + int word; + int ncmds; + int chan; /* channel index (zero based) */ + int bdid; /* board index (zero based) */ +}; + +/* +* info_sleep_stat defines +*/ +#define INFO_RUNWAIT 0x0001 +#define INFO_WOPEN 0x0002 +#define INFO_TTIOW 0x0004 +#define INFO_CH_RWAIT 0x0008 +#define INFO_CH_WEMPTY 0x0010 +#define INFO_CH_WLOW 0x0020 +#define INFO_XXBUF_BUSY 0x0040 + +#define DIGI_GETCH ('d'<<8) | 245 /* get board info */ + +/* Board type definitions */ + +#define SUBTYPE 0007 +#define T_PCXI 0000 +#define T_PCXM 0001 +#define T_PCXE 0002 +#define T_PCXR 0003 +#define T_SP 0004 +#define T_SP_PLUS 0005 +# define T_HERC 0000 +# define T_HOU 0001 +# define T_LON 0002 +# define T_CHA 0003 +#define FAMILY 0070 +#define T_COMXI 0000 +#define T_PCXX 0010 +#define T_CX 0020 +#define T_EPC 0030 +#define T_PCLITE 0040 +#define T_SPXX 0050 +#define T_AVXX 0060 +#define T_DXB 0070 +#define T_A2K_4_8 0070 +#define BUSTYPE 0700 +#define T_ISABUS 0000 +#define T_MCBUS 0100 +#define T_EISABUS 0200 +#define T_PCIBUS 0400 + +/* Board State Definitions */ + +#define BD_RUNNING 0x0 +#define BD_REASON 0x7f +#define BD_NOTFOUND 0x1 +#define BD_NOIOPORT 0x2 +#define BD_NOMEM 0x3 +#define BD_NOBIOS 0x4 +#define BD_NOFEP 0x5 +#define BD_FAILED 0x6 +#define BD_ALLOCATED 0x7 +#define BD_TRIBOOT 0x8 +#define BD_BADKME 0x80 + +#define DIGI_LOOPBACK ('d'<<8) | 252 /* Enable/disable UART internal loopback */ +#define DIGI_SPOLL ('d'<<8) | 254 /* change poller rate */ + +#define DIGI_SETCUSTOMBAUD _IOW('e', 106, int) /* Set integer baud rate */ +#define DIGI_GETCUSTOMBAUD _IOR('e', 107, int) /* Get integer baud rate */ +#define DIGI_RESET_PORT ('e'<<8) | 93 /* Reset port */ + +#endif /* DIGI_H */ diff --git a/drivers/staging/dgap/downld.c b/drivers/staging/dgap/downld.c new file mode 100644 index 000000000000..57dfd6bafcf6 --- /dev/null +++ b/drivers/staging/dgap/downld.c @@ -0,0 +1,798 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: downld.c,v 1.6 2009/01/14 14:10:54 markh Exp $ + */ + +/* +** downld.c +** +** This is the daemon that sends the fep, bios, and concentrator images +** from user space to the driver. +** BUGS: +** If the file changes in the middle of the download, you probably +** will get what you deserve. +** +*/ + +#include +#include +#include +#include +#include +#include + +#include "dgap_types.h" +#include "digi.h" +#include "dgap_fep5.h" + +#include "dgap_downld.h" + +#include +#include +#include +#include + +char *pgm; +void myperror(); + +/* +** This structure is used to keep track of the diferent images available +** to give to the driver. It is arranged so that the things that are +** constants or that have defaults are first inthe strucutre to simplify +** the table of initializers. +*/ +struct image_info { + short type; /* bios, fep, conc */ + short family; /* boards this applies to */ + short subtype; /* subtype */ + int len; /* size of image */ + char *image; /* ioctl struct + image */ + char *name; + char *fname; /* filename of binary (i.e. "asfep.bin") */ + char *pathname; /* pathname to this binary ("/etc/dgap/xrfep.bin"); */ + time_t mtime; /* Last modification time */ +}; + +#define IBIOS 0 +#define IFEP 1 +#define ICONC 2 +#define ICONFIG 3 +#define IBAD 4 + +#define DEFAULT_LOC "/lib/firmware/dgap/" + +struct image_info *image_list; +int nimages, count; + +struct image_info images[] = { +{IBIOS, T_EPC, SUBTYPE, 0, NULL, "EPC/X", "fxbios.bin", DEFAULT_LOC "fxbios.bin", 0 }, +{IFEP, T_EPC, SUBTYPE, 0, NULL, "EPC/X", "fxfep.bin", DEFAULT_LOC "fxfep.bin", 0 }, +{ICONC, T_EPC, SUBTYPE, 0, NULL, "EPC/X", "fxcon.bin", DEFAULT_LOC "fxcon.bin", 0 }, + +{IBIOS, T_CX, SUBTYPE, 0, NULL, "C/X", "cxbios.bin", DEFAULT_LOC "cxbios.bin", 0 }, +{IFEP, T_CX, SUBTYPE, 0, NULL, "C/X", "cxhost.bin", DEFAULT_LOC "cxhost.bin", 0 }, + +{IBIOS, T_CX, T_PCIBUS, 0, NULL, "C/X PCI", "cxpbios.bin", DEFAULT_LOC "cxpbios.bin", 0 }, +{IFEP, T_CX, T_PCIBUS, 0, NULL, "C/X PCI", "cxpfep.bin", DEFAULT_LOC "cxpfep.bin", 0 }, + +{ICONC, T_CX, SUBTYPE, 0, NULL, "C/X", "cxcon.bin", DEFAULT_LOC "cxcon.bin", 0 }, +{ICONC, T_CX, SUBTYPE, 0, NULL, "C/X", "ibmcxcon.bin", DEFAULT_LOC "ibmcxcon.bin", 0 }, +{ICONC, T_CX, SUBTYPE, 0, NULL, "C/X", "ibmencon.bin", DEFAULT_LOC "ibmencon.bin", 0 }, + +{IBIOS, FAMILY, T_PCXR, 0, NULL, "PCXR", "xrbios.bin", DEFAULT_LOC "xrbios.bin", 0 }, +{IFEP, FAMILY, T_PCXR, 0, NULL, "PCXR", "xrfep.bin", DEFAULT_LOC "xrfep.bin", 0 }, + +{IBIOS, T_PCLITE, SUBTYPE, 0, NULL, "X/em", "sxbios.bin", DEFAULT_LOC "sxbios.bin", 0 }, +{IFEP, T_PCLITE, SUBTYPE, 0, NULL, "X/em", "sxfep.bin", DEFAULT_LOC "sxfep.bin", 0 }, + +{IBIOS, T_EPC, T_PCIBUS, 0, NULL, "PCI", "pcibios.bin", DEFAULT_LOC "pcibios.bin", 0 }, +{IFEP, T_EPC, T_PCIBUS, 0, NULL, "PCI", "pcifep.bin", DEFAULT_LOC "pcifep.bin", 0 }, +{ICONFIG, 0, 0, 0, NULL, NULL, "dgap.conf", "/etc/dgap.conf", 0 }, + +/* IBAD/NULL entry indicating end-of-table */ + +{IBAD, 0, 0, 0, NULL, NULL, NULL, NULL, 0 } + +} ; + +int errorprint = 1; +int nodldprint = 1; +int debugflag; +int fd; + +struct downld_t *ip; /* Image pointer in current image */ +struct downld_t *dp; /* conc. download */ + + +/* + * The same for either the FEP or the BIOS. + * Append the downldio header, issue the ioctl, then free + * the buffer. Not horribly CPU efficient, but quite RAM efficient. + */ + +void squirt(int req_type, int bdid, struct image_info *ii) +{ + struct downldio *dliop; + int size_buf; + int sfd; + struct stat sb; + + /* + * If this binary comes from a file, stat it to see how + * large it is. Yes, we intentionally do this each + * time for the binary may change between loads. + */ + + if (ii->pathname) { + sfd = open(ii->pathname, O_RDONLY); + + if (sfd < 0 ) { + myperror(ii->pathname); + goto squirt_end; + } + + if (fstat(sfd, &sb) == -1 ) { + myperror(ii->pathname); + goto squirt_end; + } + + ii->len = sb.st_size ; + } + + size_buf = ii->len + sizeof(struct downldio); + + /* + * This buffer will be freed at the end of this function. It is + * not resilient and should be around only long enough for the d/l + * to happen. + */ + dliop = (struct downldio *) malloc(size_buf); + + if (dliop == NULL) { + fprintf(stderr,"%s: can't get %d bytes of memory; aborting\n", + pgm, size_buf); + exit (1); + } + + /* Now, stick the image in fepimage. This can come from either + * the compiled-in image or from the filesystem. + */ + if (ii->pathname) + read(sfd, dliop->image.fi.fepimage, ii->len); + else + memcpy(dliop ->image.fi.fepimage, ii->image, ii->len); + + dliop->req_type = req_type; + dliop->bdid = bdid; + + dliop->image.fi.len = ii->len; + + if (debugflag) + printf("sending %d bytes of %s %s from %s\n", + ii->len, + (ii->type == IFEP) ? "FEP" : (ii->type == IBIOS) ? "BIOS" : "CONFIG", + ii->name ? ii->name : "", + (ii->pathname) ? ii->pathname : "internal image" ); + + if (ioctl(fd, DIGI_DLREQ_SET, (char *) dliop) == -1) { + if(errorprint) { + fprintf(stderr, + "%s: warning - download ioctl failed\n",pgm); + errorprint = 0; + } + sleep(2); + } + +squirt_end: + + if (ii->pathname) { + close(sfd); + } + free(dliop); +} + + +/* + * See if we need to reload the download image in core + * + */ +void consider_file_rescan(struct image_info *ii) +{ + int sfd ; + int len ; + struct stat sb; + + /* This operation only makes sense when we're working from a file */ + + if (ii->pathname) { + + sfd = open (ii->pathname, O_RDONLY) ; + if (sfd < 0 ) { + myperror(ii->pathname); + exit(1) ; + } + + if( fstat(sfd,&sb) == -1 ) { + myperror(ii->pathname); + exit(1); + } + + /* If the file hasn't changed since we last did this, + * and we have not done a free() on the image, bail + */ + if (ii->image && (sb.st_mtime == ii->mtime)) + goto end_rescan; + + ii->len = len = sb.st_size ; + + /* Record the timestamp of the file */ + ii->mtime = sb.st_mtime; + + /* image should be NULL unless there is an image malloced + * in already. Before we malloc again, make sure we don't + * have a memory leak. + */ + if ( ii->image ) { + free( ii->image ); + /* ii->image = NULL; */ /* not necessary */ + } + + /* This image will be kept only long enough for the + * download to happen. After sending the last block, + * it will be freed + */ + ii->image = malloc(len) ; + + if (ii->image == NULL) { + fprintf(stderr, + "%s: can't get %d bytes of memory; aborting\n", + pgm, len); + exit (1); + } + + if (read(sfd, ii->image, len) < len) { + fprintf(stderr,"%s: read error on %s; aborting\n", + pgm, ii->pathname); + exit (1); + } + +end_rescan: + close(sfd); + + } +} + +/* + * Scan for images to match the driver requests + */ + +struct image_info * find_conc_image() +{ + int x ; + struct image_info *i = NULL ; + + for ( x = 0; x < nimages; x++ ) { + i=&image_list[x]; + + if(i->type != ICONC) + continue; + + consider_file_rescan(i) ; + + ip = (struct downld_t *) image_list[x].image; + if (ip == NULL) continue; + + /* + * When I removed Clusterport, I kept only the code that I + * was SURE wasn't ClusterPort. We may not need the next two + * lines of code. + */ + if ((dp->dl_type != 'P' ) && ( ip->dl_srev == dp->dl_srev )) + return i; + } + return NULL ; +} + + +int main(int argc, char **argv) +{ + struct downldio dlio; + int offset, bsize; + int x; + char *down, *image, *fname; + struct image_info *ii; + + pgm = argv[0]; + dp = &dlio.image.dl; /* conc. download */ + + while((argc > 2) && !strcmp(argv[1],"-d")) { + debugflag++ ; + argc-- ; + argv++ ; + } + + if(argc < 2) { + fprintf(stderr, + "usage: %s download-device [image-file] ...\n", + pgm); + exit(1); + } + + + + /* + * Daemonize, unless debugging is turned on. + */ + if (debugflag == 0) { + switch (fork()) + { + case 0: + break; + + case -1: + return 1; + + default: + return 0; + } + + setsid(); + + /* + * The child no longer needs "stdin", "stdout", or "stderr", + * and should not block processes waiting for them to close. + */ + fclose(stdin); + fclose(stdout); + fclose(stderr); + + } + + while (1) { + if( (fd = open(argv[1], O_RDWR)) == -1 ) { + sleep(1); + } + else + break; + } + + /* + ** create a list of images to search through when trying to match + ** requests from the driver. Put images from the command line in + ** the list before built in images so that the command line images + ** can override the built in ones. + */ + + /* allocate space for the list */ + + nimages = argc - 2; + + /* count the number of default list entries */ + + for (count = 0; images[count].type != IBAD; ++count) ; + + nimages += count; + + /* Really should just remove the variable "image_list".... robertl */ + image_list = images ; + + /* get the images from the command line */ + for(x = 2; x < argc; x++) { + int xx; + + /* + * strip off any leading path information for + * determining file type + */ + if( (fname = strrchr(argv[x],'/')) == NULL) + fname = argv[x]; + else + fname++; /* skip the slash */ + + for (xx = 0; xx < count; xx++) { + if (strcmp(fname, images[xx].fname) == 0 ) { + images[xx].pathname = argv[x]; + + /* image should be NULL until */ + /* space is malloced */ + images[xx].image = NULL ; + } + } + } + + sleep(3); + + /* + ** Endless loop: get a request from the fep, and service that request. + */ + for(;;) { + /* get the request */ + if (debugflag) + printf("b4 get ioctl..."); + + if (ioctl(fd,DIGI_DLREQ_GET, &dlio) == -1 ) { + if (errorprint) { + fprintf(stderr, + "%s: warning - download ioctl failed\n", + pgm); + errorprint = 0; + } + sleep(2); + } else { + if (debugflag) + printf("dlio.req_type is %d bd %d\n", + dlio.req_type,dlio.bdid); + + switch(dlio.req_type) { + case DLREQ_BIOS: + /* + ** find the bios image for this type + */ + for ( x = 0; x < nimages; x++ ) { + if(image_list[x].type != IBIOS) + continue; + + if ((dlio.image.fi.type & FAMILY) == + image_list[x].family) { + + if ( image_list[x].family == T_CX ) { + if ((dlio.image.fi.type & BUSTYPE) + == T_PCIBUS ) { + if ( image_list[x].subtype + == T_PCIBUS ) + break; + } + else { + break; + } + } + else if ( image_list[x].family == T_EPC ) { + /* If subtype of image is T_PCIBUS, it is */ + /* a PCI EPC image, so the board must */ + /* have bus type T_PCIBUS to match */ + if ((dlio.image.fi.type & BUSTYPE) + == T_PCIBUS ) { + if ( image_list[x].subtype + == T_PCIBUS ) + break; + } + else { + /* NON PCI EPC doesn't use PCI image */ + if ( image_list[x].subtype + != T_PCIBUS ) + break; + } + } + else + break; + } + else if ((dlio.image.fi.type & SUBTYPE) == image_list[x].subtype) { + /* PCXR board will break out of the loop here */ + if ( image_list[x].subtype == T_PCXR ) { + break; + } + } + } + + if ( x >= nimages) { + /* + ** no valid images exist + */ + if(nodldprint) { + fprintf(stderr, + "%s: cannot find correct BIOS image\n", + pgm); + nodldprint = 0; + } + dlio.image.fi.type = -1; + if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1) { + if (errorprint) { + fprintf(stderr, + "%s: warning - download ioctl failed\n", + pgm); + errorprint = 0; + } + sleep(2); + } + break; + } + squirt(dlio.req_type, dlio.bdid, &image_list[x]); + break ; + + case DLREQ_FEP: + /* + ** find the fep image for this type + */ + for ( x = 0; x < nimages; x++ ) { + if(image_list[x].type != IFEP) + continue; + if( (dlio.image.fi.type & FAMILY) == + image_list[x].family ) { + if ( image_list[x].family == T_CX ) { + /* C/X PCI board */ + if ((dlio.image.fi.type & BUSTYPE) + == T_PCIBUS ) { + if ( image_list[x].subtype + == T_PCIBUS ) + break; + } + else { + /* Regular CX */ + break; + } + } + else if ( image_list[x].family == T_EPC ) { + /* If subtype of image is T_PCIBUS, it is */ + /* a PCI EPC image, so the board must */ + /* have bus type T_PCIBUS to match */ + if ((dlio.image.fi.type & BUSTYPE) + == T_PCIBUS ) { + if ( image_list[x].subtype + == T_PCIBUS ) + break; + } + else { + /* NON PCI EPC doesn't use PCI image */ + if ( image_list[x].subtype + != T_PCIBUS ) + break; + } + } + else + break; + } + else if ((dlio.image.fi.type & SUBTYPE) == image_list[x].subtype) { + /* PCXR board will break out of the loop here */ + if ( image_list[x].subtype == T_PCXR ) { + break; + } + } + } + + if ( x >= nimages) { + /* + ** no valid images exist + */ + if(nodldprint) { + fprintf(stderr, + "%s: cannot find correct FEP image\n", + pgm); + nodldprint = 0; + } + dlio.image.fi.type=-1; + if( ioctl(fd,DIGI_DLREQ_SET,&dlio) == -1 ) { + if(errorprint) { + fprintf(stderr, + "%s: warning - download ioctl failed\n", + pgm); + errorprint=0; + } + sleep(2); + } + break; + } + squirt(dlio.req_type, dlio.bdid, &image_list[x]); + break; + + case DLREQ_DEVCREATE: + { + char string[1024]; +#if 0 + sprintf(string, "%s /proc/dgap/%d/mknod", DEFSHELL, dlio.bdid); +#endif + sprintf(string, "%s /usr/sbin/dgap_updatedevs %d", DEFSHELL, dlio.bdid); + system(string); + + if (debugflag) + printf("Created Devices.\n"); + if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1 ) { + if(errorprint) { + fprintf(stderr, "%s: warning - DEVCREATE ioctl failed\n",pgm); + errorprint = 0; + } + sleep(2); + } + if (debugflag) + printf("After ioctl set - Created Device.\n"); + } + + break; + + case DLREQ_CONFIG: + for ( x = 0; x < nimages; x++ ) { + if(image_list[x].type != ICONFIG) + continue; + else + break; + } + + if ( x >= nimages) { + /* + ** no valid images exist + */ + if(nodldprint) { + fprintf(stderr, + "%s: cannot find correct CONFIG image\n", + pgm); + nodldprint = 0; + } + dlio.image.fi.type=-1; + if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1 ) { + if(errorprint) { + fprintf(stderr, + "%s: warning - download ioctl failed\n", + pgm); + errorprint=0; + } + sleep(2); + } + break; + } + + squirt(dlio.req_type, dlio.bdid, &image_list[x]); + break; + + case DLREQ_CONC: + /* + ** find the image needed for this download + */ + if ( dp->dl_seq == 0 ) { + /* + ** find image for hardware rev range + */ + for ( x = 0; x < nimages; x++ ) { + ii=&image_list[x]; + + if(image_list[x].type != ICONC) + continue; + + consider_file_rescan(ii) ; + + ip = (struct downld_t *) image_list[x].image; + if (ip == NULL) continue; + + /* + * When I removed Clusterport, I kept only the + * code that I was SURE wasn't ClusterPort. + * We may not need the next four lines of code. + */ + + if ((dp->dl_type != 'P' ) && + (ip->dl_lrev <= dp->dl_lrev ) && + ( dp->dl_lrev <= ip->dl_hrev)) + break; + } + + if ( x >= nimages ) { + /* + ** No valid images exist + */ + if(nodldprint) { + fprintf(stderr, + "%s: cannot find correct download image %d\n", + pgm, dp->dl_lrev); + nodldprint=0; + } + continue; + } + + } else { + /* + ** find image version required + */ + if ((ii = find_conc_image()) == NULL ) { + /* + ** No valid images exist + */ + fprintf(stderr, + "%s: can't find rest of download image??\n", + pgm); + continue; + } + } + + /* + ** download block of image + */ + + offset = 1024 * dp->dl_seq; + + /* + ** test if block requested within image + */ + if ( offset < ii->len ) { + + /* + ** if it is, determine block size, set segment, + ** set size, set pointers, and copy block + */ + if (( bsize = ii->len - offset ) > 1024 ) + bsize = 1024; + + /* + ** copy image version info to download area + */ + dp->dl_srev = ip->dl_srev; + dp->dl_lrev = ip->dl_lrev; + dp->dl_hrev = ip->dl_hrev; + + dp->dl_seg = (64 * dp->dl_seq) + ip->dl_seg; + dp->dl_size = bsize; + + down = (char *)&dp->dl_data[0]; + image = (char *)((char *)ip + offset); + + memcpy(down, image, bsize); + } + else { + /* + ** Image has been downloaded, set segment and + ** size to indicate no more blocks + */ + dp->dl_seg = ip->dl_seg; + dp->dl_size = 0; + + /* Now, we can release the concentrator */ + /* image from memory if we're running */ + /* from filesystem images */ + + if (ii->pathname) + if (ii->image) { + free(ii->image); + ii->image = NULL ; + } + } + + if (debugflag) + printf( + "sending conc dl section %d to %s from %s\n", + dp->dl_seq, ii->name, + ii->pathname ? ii->pathname : "Internal Image"); + + if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1 ) { + if (errorprint) { + fprintf(stderr, + "%s: warning - download ioctl failed\n", + pgm); + errorprint=0; + } + sleep(2); + } + break; + } /* switch */ + } + if (debugflag > 1) { + printf("pausing: "); fflush(stdout); + fflush(stdin); + while(getchar() != '\n'); + printf("continuing\n"); + } + } +} + +/* +** myperror() +** +** Same as normal perror(), but places the program name at the begining +** of the message. +*/ +void myperror(char *s) +{ + fprintf(stderr,"%s: %s: %s.\n",pgm, s, strerror(errno)); +} -- 2.39.5