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