Commit c84b8b50 authored by Scott_Kilau@digi.com's avatar Scott_Kilau@digi.com Committed by Greg Kroah-Hartman

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 <www.digi.com>.
Signed-off-by: default avatarLidza Louina <lidza.louina@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6b5ad9d2
config DGAP
tristate "Digi EPCA PCI products"
default n
depends on TTY
---help---
Driver for the Digi International EPCA PCI based product line
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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h> /* For udelay */
#include <asm/uaccess.h> /* For copy_from_user/copy_to_user */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
#include <linux/sched.h>
#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");
}
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/version.h> /* To get the current Linux version */
#include <linux/types.h> /* To pick up the varions Linux types */
#include <linux/tty.h> /* To pick up the various tty structs/defines */
#include <linux/interrupt.h> /* 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/<board> 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/<board>/<channel> 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h> /* For udelay */
#include <asm/uaccess.h> /* For copy_from_user/copy_to_user */
#include <linux/tty.h>
#include <linux/tty_flip.h> /* For tty_schedule_flip */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
#include <linux/sched.h>
#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);
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2004 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 */
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/ctype.h>
#include <linux/sched.h> /* For jiffies, task states */
#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
#include <linux/pci.h>
#include <linux/serial_reg.h>
#include <linux/termios.h>
#include <asm/uaccess.h> /* For copy_from_user/copy_to_user */
#include <asm/io.h> /* 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;
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#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 "<NULL>";
}
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;
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/sched.h> /* For jiffies, task states */
#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/proc_fs.h>
#include <linux/serial_reg.h>
#include <linux/sched.h> /* For in_egroup_p() */
#include <linux/string.h>
#include <asm/uaccess.h> /* For copy_from_user/copy_to_user */
#include <linux/cred.h>
#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/<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);
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/<board>/<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);
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);
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 */
/*
* Copyright 2004 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/serial_reg.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/kdev_t.h>
#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);
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/device.h>
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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/sched.h> /* For jiffies, task states */
#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
#include <linux/vmalloc.h>
#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);
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <linux/kernel.h>
#include <linux/version.h>
#include <linux/sched.h> /* For jiffies, task states */
#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
#include <linux/delay.h> /* For udelay */
#include <asm/uaccess.h> /* For copy_from_user/copy_to_user */
#include <asm/io.h> /* For read[bwl]/write[bwl] */
#include <linux/pci.h>
#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);
}
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 */
/*
* Copyright 2003 Digi International (www.digi.com)
* Scott H Kilau <Scott_Kilau at digi dot com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* 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 <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include "dgap_types.h"
#include "digi.h"
#include "dgap_fep5.h"
#include "dgap_downld.h"
#include <string.h>
#include <malloc.h>
#include <stddef.h>
#include <unistd.h>
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));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment