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