| /* |
| * 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 |
| * Neo and ClassicBoard based product lines. |
| * |
| ************************************************************************ |
| */ |
| #include <linux/kernel.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/serial_reg.h> |
| #include <linux/termios.h> |
| #include <asm/uaccess.h> /* For copy_from_user/copy_to_user */ |
| |
| #include "dgnc_driver.h" |
| #include "dgnc_pci.h" |
| #include "dgnc_kcompat.h" /* Kernel 2.4/2.6 compat includes */ |
| #include "dgnc_mgmt.h" |
| #include "dpacompat.h" |
| |
| |
| /* Our "in use" variables, to enforce 1 open only */ |
| static int dgnc_mgmt_in_use[MAXMGMTDEVICES]; |
| |
| |
| /* |
| * dgnc_mgmt_open() |
| * |
| * Open the mgmt/downld/dpa device |
| */ |
| int dgnc_mgmt_open(struct inode *inode, struct file *file) |
| { |
| unsigned long lock_flags; |
| unsigned int minor = iminor(inode); |
| |
| DPR_MGMT(("dgnc_mgmt_open start.\n")); |
| |
| DGNC_LOCK(dgnc_global_lock, lock_flags); |
| |
| /* mgmt device */ |
| if (minor < MAXMGMTDEVICES) { |
| /* Only allow 1 open at a time on mgmt device */ |
| if (dgnc_mgmt_in_use[minor]) { |
| DGNC_UNLOCK(dgnc_global_lock, lock_flags); |
| return -EBUSY; |
| } |
| dgnc_mgmt_in_use[minor]++; |
| } |
| else { |
| DGNC_UNLOCK(dgnc_global_lock, lock_flags); |
| return -ENXIO; |
| } |
| |
| DGNC_UNLOCK(dgnc_global_lock, lock_flags); |
| |
| DPR_MGMT(("dgnc_mgmt_open finish.\n")); |
| |
| return 0; |
| } |
| |
| |
| /* |
| * dgnc_mgmt_close() |
| * |
| * Open the mgmt/dpa device |
| */ |
| int dgnc_mgmt_close(struct inode *inode, struct file *file) |
| { |
| unsigned long lock_flags; |
| unsigned int minor = iminor(inode); |
| |
| DPR_MGMT(("dgnc_mgmt_close start.\n")); |
| |
| DGNC_LOCK(dgnc_global_lock, lock_flags); |
| |
| /* mgmt device */ |
| if (minor < MAXMGMTDEVICES) { |
| if (dgnc_mgmt_in_use[minor]) { |
| dgnc_mgmt_in_use[minor] = 0; |
| } |
| } |
| DGNC_UNLOCK(dgnc_global_lock, lock_flags); |
| |
| DPR_MGMT(("dgnc_mgmt_close finish.\n")); |
| |
| return 0; |
| } |
| |
| |
| /* |
| * dgnc_mgmt_ioctl() |
| * |
| * ioctl the mgmt/dpa device |
| */ |
| |
| long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| unsigned long lock_flags; |
| void __user *uarg = (void __user *) arg; |
| |
| DPR_MGMT(("dgnc_mgmt_ioctl start.\n")); |
| |
| switch (cmd) { |
| |
| 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; |
| |
| DGNC_LOCK(dgnc_global_lock, lock_flags); |
| |
| ddi.dinfo_nboards = dgnc_NumBoards; |
| sprintf(ddi.dinfo_version, "%s", DG_PART); |
| |
| DGNC_UNLOCK(dgnc_global_lock, lock_flags); |
| |
| DPR_MGMT(("DIGI_GETDD returning numboards: %d version: %s\n", |
| ddi.dinfo_nboards, ddi.dinfo_version)); |
| |
| if (copy_to_user(uarg, &ddi, sizeof (ddi))) |
| return -EFAULT; |
| |
| break; |
| } |
| |
| case DIGI_GETBD: |
| { |
| int brd; |
| |
| struct digi_info di; |
| |
| if (copy_from_user(&brd, uarg, sizeof(int))) { |
| return -EFAULT; |
| } |
| |
| DPR_MGMT(("DIGI_GETBD asking about board: %d\n", brd)); |
| |
| if ((brd < 0) || (brd > dgnc_NumBoards) || (dgnc_NumBoards == 0)) |
| return -ENODEV; |
| |
| memset(&di, 0, sizeof(di)); |
| |
| di.info_bdnum = brd; |
| |
| DGNC_LOCK(dgnc_Board[brd]->bd_lock, lock_flags); |
| |
| di.info_bdtype = dgnc_Board[brd]->dpatype; |
| di.info_bdstate = dgnc_Board[brd]->dpastatus; |
| di.info_ioport = 0; |
| di.info_physaddr = (ulong) dgnc_Board[brd]->membase; |
| di.info_physsize = (ulong) dgnc_Board[brd]->membase - dgnc_Board[brd]->membase_end; |
| if (dgnc_Board[brd]->state != BOARD_FAILED) |
| di.info_nports = dgnc_Board[brd]->nasync; |
| else |
| di.info_nports = 0; |
| |
| DGNC_UNLOCK(dgnc_Board[brd]->bd_lock, lock_flags); |
| |
| DPR_MGMT(("DIGI_GETBD returning type: %x state: %x ports: %x size: %x\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_GET_NI_INFO: |
| { |
| struct channel_t *ch; |
| struct ni_info ni; |
| uchar mstat = 0; |
| uint board = 0; |
| uint channel = 0; |
| |
| if (copy_from_user(&ni, uarg, sizeof(ni))) { |
| return -EFAULT; |
| } |
| |
| DPR_MGMT(("DIGI_GETBD asking about board: %d channel: %d\n", |
| ni.board, ni.channel)); |
| |
| board = ni.board; |
| channel = ni.channel; |
| |
| /* Verify boundaries on board */ |
| if ((board > dgnc_NumBoards) || (dgnc_NumBoards == 0)) |
| return -ENODEV; |
| |
| /* Verify boundaries on channel */ |
| if ((channel < 0) || (channel > dgnc_Board[board]->nasync)) |
| return -ENODEV; |
| |
| ch = dgnc_Board[board]->channels[channel]; |
| |
| if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) |
| return -ENODEV; |
| |
| memset(&ni, 0, sizeof(ni)); |
| ni.board = board; |
| ni.channel = channel; |
| |
| DGNC_LOCK(ch->ch_lock, lock_flags); |
| |
| mstat = (ch->ch_mostat | ch->ch_mistat); |
| |
| if (mstat & UART_MCR_DTR) { |
| ni.mstat |= TIOCM_DTR; |
| ni.dtr = TIOCM_DTR; |
| } |
| if (mstat & UART_MCR_RTS) { |
| ni.mstat |= TIOCM_RTS; |
| ni.rts = TIOCM_RTS; |
| } |
| if (mstat & UART_MSR_CTS) { |
| ni.mstat |= TIOCM_CTS; |
| ni.cts = TIOCM_CTS; |
| } |
| if (mstat & UART_MSR_RI) { |
| ni.mstat |= TIOCM_RI; |
| ni.ri = TIOCM_RI; |
| } |
| if (mstat & UART_MSR_DCD) { |
| ni.mstat |= TIOCM_CD; |
| ni.dcd = TIOCM_CD; |
| } |
| if (mstat & UART_MSR_DSR) |
| ni.mstat |= TIOCM_DSR; |
| |
| ni.iflag = ch->ch_c_iflag; |
| ni.oflag = ch->ch_c_oflag; |
| ni.cflag = ch->ch_c_cflag; |
| ni.lflag = ch->ch_c_lflag; |
| |
| if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) |
| ni.hflow = 1; |
| else |
| ni.hflow = 0; |
| |
| if ((ch->ch_flags & CH_STOPI) || (ch->ch_flags & CH_FORCED_STOPI)) |
| ni.recv_stopped = 1; |
| else |
| ni.recv_stopped = 0; |
| |
| if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP)) |
| ni.xmit_stopped = 1; |
| else |
| ni.xmit_stopped = 0; |
| |
| ni.curtx = ch->ch_txcount; |
| ni.currx = ch->ch_rxcount; |
| |
| ni.baud = ch->ch_old_baud; |
| |
| DGNC_UNLOCK(ch->ch_lock, lock_flags); |
| |
| if (copy_to_user(uarg, &ni, sizeof(ni))) |
| return -EFAULT; |
| |
| break; |
| } |
| |
| |
| } |
| |
| DPR_MGMT(("dgnc_mgmt_ioctl finish.\n")); |
| |
| return 0; |
| } |