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