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 
16 /************************************************************************
17  *
18  * This file implements the mgmt functionality for the
19  * Neo and ClassicBoard based product lines.
20  *
21  ************************************************************************
22  */
23 #include <linux/kernel.h>
24 #include <linux/ctype.h>
25 #include <linux/sched.h>	/* For jiffies, task states */
26 #include <linux/interrupt.h>	/* For tasklet and interrupt structs/defines */
27 #include <linux/serial_reg.h>
28 #include <linux/termios.h>
29 #include <linux/uaccess.h>	/* For copy_from_user/copy_to_user */
30 
31 #include "dgnc_driver.h"
32 #include "dgnc_pci.h"
33 #include "dgnc_mgmt.h"
34 
35 /* Our "in use" variables, to enforce 1 open only */
36 static int dgnc_mgmt_in_use[MAXMGMTDEVICES];
37 
38 /*
39  * dgnc_mgmt_open()
40  *
41  * Open the mgmt/downld/dpa device
42  */
dgnc_mgmt_open(struct inode * inode,struct file * file)43 int dgnc_mgmt_open(struct inode *inode, struct file *file)
44 {
45 	unsigned long flags;
46 	unsigned int minor = iminor(inode);
47 
48 	spin_lock_irqsave(&dgnc_global_lock, flags);
49 
50 	/* mgmt device */
51 	if (minor < MAXMGMTDEVICES) {
52 		/* Only allow 1 open at a time on mgmt device */
53 		if (dgnc_mgmt_in_use[minor]) {
54 			spin_unlock_irqrestore(&dgnc_global_lock, flags);
55 			return -EBUSY;
56 		}
57 		dgnc_mgmt_in_use[minor]++;
58 	} else {
59 		spin_unlock_irqrestore(&dgnc_global_lock, flags);
60 		return -ENXIO;
61 	}
62 
63 	spin_unlock_irqrestore(&dgnc_global_lock, flags);
64 
65 	return 0;
66 }
67 
68 /*
69  * dgnc_mgmt_close()
70  *
71  * Open the mgmt/dpa device
72  */
dgnc_mgmt_close(struct inode * inode,struct file * file)73 int dgnc_mgmt_close(struct inode *inode, struct file *file)
74 {
75 	unsigned long flags;
76 	unsigned int minor = iminor(inode);
77 
78 	spin_lock_irqsave(&dgnc_global_lock, flags);
79 
80 	/* mgmt device */
81 	if (minor < MAXMGMTDEVICES) {
82 		if (dgnc_mgmt_in_use[minor])
83 			dgnc_mgmt_in_use[minor] = 0;
84 	}
85 	spin_unlock_irqrestore(&dgnc_global_lock, flags);
86 
87 	return 0;
88 }
89 
90 /*
91  * dgnc_mgmt_ioctl()
92  *
93  * ioctl the mgmt/dpa device
94  */
95 
dgnc_mgmt_ioctl(struct file * file,unsigned int cmd,unsigned long arg)96 long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
97 {
98 	unsigned long flags;
99 	void __user *uarg = (void __user *)arg;
100 
101 	switch (cmd) {
102 	case DIGI_GETDD:
103 	{
104 		/*
105 		 * This returns the total number of boards
106 		 * in the system, as well as driver version
107 		 * and has space for a reserved entry
108 		 */
109 		struct digi_dinfo ddi;
110 
111 		spin_lock_irqsave(&dgnc_global_lock, flags);
112 
113 		memset(&ddi, 0, sizeof(ddi));
114 		ddi.dinfo_nboards = dgnc_NumBoards;
115 		sprintf(ddi.dinfo_version, "%s", DG_PART);
116 
117 		spin_unlock_irqrestore(&dgnc_global_lock, flags);
118 
119 		if (copy_to_user(uarg, &ddi, sizeof(ddi)))
120 			return -EFAULT;
121 
122 		break;
123 	}
124 
125 	case DIGI_GETBD:
126 	{
127 		int brd;
128 
129 		struct digi_info di;
130 
131 		if (copy_from_user(&brd, uarg, sizeof(int)))
132 			return -EFAULT;
133 
134 		if (brd < 0 || brd >= dgnc_NumBoards)
135 			return -ENODEV;
136 
137 		memset(&di, 0, sizeof(di));
138 
139 		di.info_bdnum = brd;
140 
141 		spin_lock_irqsave(&dgnc_Board[brd]->bd_lock, flags);
142 
143 		di.info_bdtype = dgnc_Board[brd]->dpatype;
144 		di.info_bdstate = dgnc_Board[brd]->dpastatus;
145 		di.info_ioport = 0;
146 		di.info_physaddr = (ulong)dgnc_Board[brd]->membase;
147 		di.info_physsize = (ulong)dgnc_Board[brd]->membase
148 			- dgnc_Board[brd]->membase_end;
149 		if (dgnc_Board[brd]->state != BOARD_FAILED)
150 			di.info_nports = dgnc_Board[brd]->nasync;
151 		else
152 			di.info_nports = 0;
153 
154 		spin_unlock_irqrestore(&dgnc_Board[brd]->bd_lock, flags);
155 
156 		if (copy_to_user(uarg, &di, sizeof(di)))
157 			return -EFAULT;
158 
159 		break;
160 	}
161 
162 	case DIGI_GET_NI_INFO:
163 	{
164 		struct channel_t *ch;
165 		struct ni_info ni;
166 		unsigned char mstat = 0;
167 		uint board = 0;
168 		uint channel = 0;
169 
170 		if (copy_from_user(&ni, uarg, sizeof(ni)))
171 			return -EFAULT;
172 
173 		board = ni.board;
174 		channel = ni.channel;
175 
176 		/* Verify boundaries on board */
177 		if (board >= dgnc_NumBoards)
178 			return -ENODEV;
179 
180 		/* Verify boundaries on channel */
181 		if (channel >= dgnc_Board[board]->nasync)
182 			return -ENODEV;
183 
184 		ch = dgnc_Board[board]->channels[channel];
185 
186 		if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
187 			return -ENODEV;
188 
189 		memset(&ni, 0, sizeof(ni));
190 		ni.board = board;
191 		ni.channel = channel;
192 
193 		spin_lock_irqsave(&ch->ch_lock, flags);
194 
195 		mstat = (ch->ch_mostat | ch->ch_mistat);
196 
197 		if (mstat & UART_MCR_DTR) {
198 			ni.mstat |= TIOCM_DTR;
199 			ni.dtr = TIOCM_DTR;
200 		}
201 		if (mstat & UART_MCR_RTS) {
202 			ni.mstat |= TIOCM_RTS;
203 			ni.rts = TIOCM_RTS;
204 		}
205 		if (mstat & UART_MSR_CTS) {
206 			ni.mstat |= TIOCM_CTS;
207 			ni.cts = TIOCM_CTS;
208 		}
209 		if (mstat & UART_MSR_RI) {
210 			ni.mstat |= TIOCM_RI;
211 			ni.ri = TIOCM_RI;
212 		}
213 		if (mstat & UART_MSR_DCD) {
214 			ni.mstat |= TIOCM_CD;
215 			ni.dcd = TIOCM_CD;
216 		}
217 		if (mstat & UART_MSR_DSR)
218 			ni.mstat |= TIOCM_DSR;
219 
220 		ni.iflag = ch->ch_c_iflag;
221 		ni.oflag = ch->ch_c_oflag;
222 		ni.cflag = ch->ch_c_cflag;
223 		ni.lflag = ch->ch_c_lflag;
224 
225 		if (ch->ch_digi.digi_flags & CTSPACE ||
226 		    ch->ch_c_cflag & CRTSCTS)
227 			ni.hflow = 1;
228 		else
229 			ni.hflow = 0;
230 
231 		if ((ch->ch_flags & CH_STOPI) ||
232 		    (ch->ch_flags & CH_FORCED_STOPI))
233 			ni.recv_stopped = 1;
234 		else
235 			ni.recv_stopped = 0;
236 
237 		if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP))
238 			ni.xmit_stopped = 1;
239 		else
240 			ni.xmit_stopped = 0;
241 
242 		ni.curtx = ch->ch_txcount;
243 		ni.currx = ch->ch_rxcount;
244 
245 		ni.baud = ch->ch_old_baud;
246 
247 		spin_unlock_irqrestore(&ch->ch_lock, flags);
248 
249 		if (copy_to_user(uarg, &ni, sizeof(ni)))
250 			return -EFAULT;
251 
252 		break;
253 	}
254 	}
255 
256 	return 0;
257 }
258