1/* 2 * Copyright 2003 Digi International (www.digi.com) 3 * Scott H Kilau <Scott_Kilau at digi dot com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 12 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 13 * PURPOSE. See the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 * 19 * 20 * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! 21 * 22 * This is shared code between Digi's CVS archive and the 23 * Linux Kernel sources. 24 * Changing the source just for reformatting needlessly breaks 25 * our CVS diff history. 26 * 27 * Send any bug fixes/changes to: Eng.Linux at digi dot com. 28 * Thank you. 29 * 30 */ 31 32/************************************************************************ 33 * 34 * This file implements the mgmt functionality for the 35 * Neo and ClassicBoard based product lines. 36 * 37 ************************************************************************ 38 */ 39#include <linux/kernel.h> 40#include <linux/ctype.h> 41#include <linux/sched.h> /* For jiffies, task states */ 42#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */ 43#include <linux/serial_reg.h> 44#include <linux/termios.h> 45#include <linux/uaccess.h> /* For copy_from_user/copy_to_user */ 46 47#include "dgnc_driver.h" 48#include "dgnc_pci.h" 49#include "dgnc_kcompat.h" /* Kernel 2.4/2.6 compat includes */ 50#include "dgnc_mgmt.h" 51#include "dpacompat.h" 52 53 54/* Our "in use" variables, to enforce 1 open only */ 55static int dgnc_mgmt_in_use[MAXMGMTDEVICES]; 56 57 58/* 59 * dgnc_mgmt_open() 60 * 61 * Open the mgmt/downld/dpa device 62 */ 63int dgnc_mgmt_open(struct inode *inode, struct file *file) 64{ 65 unsigned long flags; 66 unsigned int minor = iminor(inode); 67 68 spin_lock_irqsave(&dgnc_global_lock, flags); 69 70 /* mgmt device */ 71 if (minor < MAXMGMTDEVICES) { 72 /* Only allow 1 open at a time on mgmt device */ 73 if (dgnc_mgmt_in_use[minor]) { 74 spin_unlock_irqrestore(&dgnc_global_lock, flags); 75 return -EBUSY; 76 } 77 dgnc_mgmt_in_use[minor]++; 78 } else { 79 spin_unlock_irqrestore(&dgnc_global_lock, flags); 80 return -ENXIO; 81 } 82 83 spin_unlock_irqrestore(&dgnc_global_lock, flags); 84 85 return 0; 86} 87 88 89/* 90 * dgnc_mgmt_close() 91 * 92 * Open the mgmt/dpa device 93 */ 94int dgnc_mgmt_close(struct inode *inode, struct file *file) 95{ 96 unsigned long flags; 97 unsigned int minor = iminor(inode); 98 99 spin_lock_irqsave(&dgnc_global_lock, flags); 100 101 /* mgmt device */ 102 if (minor < MAXMGMTDEVICES) { 103 if (dgnc_mgmt_in_use[minor]) 104 dgnc_mgmt_in_use[minor] = 0; 105 } 106 spin_unlock_irqrestore(&dgnc_global_lock, flags); 107 108 return 0; 109} 110 111 112/* 113 * dgnc_mgmt_ioctl() 114 * 115 * ioctl the mgmt/dpa device 116 */ 117 118long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 119{ 120 unsigned long flags; 121 void __user *uarg = (void __user *) arg; 122 123 switch (cmd) { 124 125 case DIGI_GETDD: 126 { 127 /* 128 * This returns the total number of boards 129 * in the system, as well as driver version 130 * and has space for a reserved entry 131 */ 132 struct digi_dinfo ddi; 133 134 spin_lock_irqsave(&dgnc_global_lock, flags); 135 136 ddi.dinfo_nboards = dgnc_NumBoards; 137 sprintf(ddi.dinfo_version, "%s", DG_PART); 138 139 spin_unlock_irqrestore(&dgnc_global_lock, flags); 140 141 if (copy_to_user(uarg, &ddi, sizeof(ddi))) 142 return -EFAULT; 143 144 break; 145 } 146 147 case DIGI_GETBD: 148 { 149 int brd; 150 151 struct digi_info di; 152 153 if (copy_from_user(&brd, uarg, sizeof(int))) 154 return -EFAULT; 155 156 if ((brd < 0) || (brd > dgnc_NumBoards) || 157 (dgnc_NumBoards == 0)) 158 return -ENODEV; 159 160 memset(&di, 0, sizeof(di)); 161 162 di.info_bdnum = brd; 163 164 spin_lock_irqsave(&dgnc_Board[brd]->bd_lock, flags); 165 166 di.info_bdtype = dgnc_Board[brd]->dpatype; 167 di.info_bdstate = dgnc_Board[brd]->dpastatus; 168 di.info_ioport = 0; 169 di.info_physaddr = (ulong) dgnc_Board[brd]->membase; 170 di.info_physsize = (ulong) dgnc_Board[brd]->membase - dgnc_Board[brd]->membase_end; 171 if (dgnc_Board[brd]->state != BOARD_FAILED) 172 di.info_nports = dgnc_Board[brd]->nasync; 173 else 174 di.info_nports = 0; 175 176 spin_unlock_irqrestore(&dgnc_Board[brd]->bd_lock, flags); 177 178 if (copy_to_user(uarg, &di, sizeof(di))) 179 return -EFAULT; 180 181 break; 182 } 183 184 case DIGI_GET_NI_INFO: 185 { 186 struct channel_t *ch; 187 struct ni_info ni; 188 unsigned char mstat = 0; 189 uint board = 0; 190 uint channel = 0; 191 192 if (copy_from_user(&ni, uarg, sizeof(ni))) 193 return -EFAULT; 194 195 board = ni.board; 196 channel = ni.channel; 197 198 /* Verify boundaries on board */ 199 if ((board > dgnc_NumBoards) || (dgnc_NumBoards == 0)) 200 return -ENODEV; 201 202 /* Verify boundaries on channel */ 203 if ((channel < 0) || (channel > dgnc_Board[board]->nasync)) 204 return -ENODEV; 205 206 ch = dgnc_Board[board]->channels[channel]; 207 208 if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) 209 return -ENODEV; 210 211 memset(&ni, 0, sizeof(ni)); 212 ni.board = board; 213 ni.channel = channel; 214 215 spin_lock_irqsave(&ch->ch_lock, flags); 216 217 mstat = (ch->ch_mostat | ch->ch_mistat); 218 219 if (mstat & UART_MCR_DTR) { 220 ni.mstat |= TIOCM_DTR; 221 ni.dtr = TIOCM_DTR; 222 } 223 if (mstat & UART_MCR_RTS) { 224 ni.mstat |= TIOCM_RTS; 225 ni.rts = TIOCM_RTS; 226 } 227 if (mstat & UART_MSR_CTS) { 228 ni.mstat |= TIOCM_CTS; 229 ni.cts = TIOCM_CTS; 230 } 231 if (mstat & UART_MSR_RI) { 232 ni.mstat |= TIOCM_RI; 233 ni.ri = TIOCM_RI; 234 } 235 if (mstat & UART_MSR_DCD) { 236 ni.mstat |= TIOCM_CD; 237 ni.dcd = TIOCM_CD; 238 } 239 if (mstat & UART_MSR_DSR) 240 ni.mstat |= TIOCM_DSR; 241 242 ni.iflag = ch->ch_c_iflag; 243 ni.oflag = ch->ch_c_oflag; 244 ni.cflag = ch->ch_c_cflag; 245 ni.lflag = ch->ch_c_lflag; 246 247 if (ch->ch_digi.digi_flags & CTSPACE || 248 ch->ch_c_cflag & CRTSCTS) 249 ni.hflow = 1; 250 else 251 ni.hflow = 0; 252 253 if ((ch->ch_flags & CH_STOPI) || 254 (ch->ch_flags & CH_FORCED_STOPI)) 255 ni.recv_stopped = 1; 256 else 257 ni.recv_stopped = 0; 258 259 if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP)) 260 ni.xmit_stopped = 1; 261 else 262 ni.xmit_stopped = 0; 263 264 ni.curtx = ch->ch_txcount; 265 ni.currx = ch->ch_rxcount; 266 267 ni.baud = ch->ch_old_baud; 268 269 spin_unlock_irqrestore(&ch->ch_lock, flags); 270 271 if (copy_to_user(uarg, &ni, sizeof(ni))) 272 return -EFAULT; 273 274 break; 275 } 276 277 278 } 279 280 return 0; 281} 282