]>
Commit | Line | Data |
---|---|---|
0b52b749 BP |
1 | /* |
2 | * | |
3 | * Copyright 1999 Digi International (www.digi.com) | |
4 | * James Puzzo <jamesp at digi dot com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the | |
13 | * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
14 | * PURPOSE. See the GNU General Public License for more details. | |
15 | * | |
16 | */ | |
17 | ||
18 | /* | |
19 | * | |
20 | * Filename: | |
21 | * | |
22 | * dgrp_dpa_ops.c | |
23 | * | |
24 | * Description: | |
25 | * | |
26 | * Handle the file operations required for the "dpa" devices. | |
27 | * Includes those functions required to register the "dpa" devices | |
28 | * in "/proc". | |
29 | * | |
30 | * Author: | |
31 | * | |
32 | * James A. Puzzo | |
33 | * | |
34 | */ | |
35 | ||
36 | #include <linux/module.h> | |
37 | #include <linux/proc_fs.h> | |
38 | #include <linux/tty.h> | |
39 | #include <linux/poll.h> | |
40 | #include <linux/cred.h> | |
41 | #include <linux/sched.h> | |
42 | #include <linux/ratelimit.h> | |
43 | #include <asm/unaligned.h> | |
44 | ||
45 | #include "dgrp_common.h" | |
46 | ||
47 | /* File operation declarations */ | |
48 | static int dgrp_dpa_open(struct inode *, struct file *); | |
49 | static int dgrp_dpa_release(struct inode *, struct file *); | |
50 | static ssize_t dgrp_dpa_read(struct file *, char __user *, size_t, loff_t *); | |
51 | static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd, | |
52 | unsigned long arg); | |
53 | static unsigned int dgrp_dpa_select(struct file *, struct poll_table_struct *); | |
54 | ||
af064cdd | 55 | const struct file_operations dgrp_dpa_ops = { |
0b52b749 BP |
56 | .owner = THIS_MODULE, |
57 | .read = dgrp_dpa_read, | |
58 | .poll = dgrp_dpa_select, | |
59 | .unlocked_ioctl = dgrp_dpa_ioctl, | |
60 | .open = dgrp_dpa_open, | |
61 | .release = dgrp_dpa_release, | |
62 | }; | |
63 | ||
0b52b749 BP |
64 | struct digi_node { |
65 | uint nd_state; /* Node state: 1 = up, 0 = down. */ | |
66 | uint nd_chan_count; /* Number of channels found */ | |
67 | uint nd_tx_byte; /* Tx data count */ | |
68 | uint nd_rx_byte; /* RX data count */ | |
69 | u8 nd_ps_desc[MAX_DESC_LEN]; /* Description from PS */ | |
70 | }; | |
71 | ||
72 | #define DIGI_GETNODE (('d'<<8) | 249) /* get board info */ | |
73 | ||
74 | ||
75 | struct digi_chan { | |
76 | uint ch_port; /* Port number to get info on */ | |
77 | uint ch_open; /* 1 if open, 0 if not */ | |
78 | uint ch_txcount; /* TX data count */ | |
79 | uint ch_rxcount; /* RX data count */ | |
80 | uint ch_s_brate; /* Realport BRATE */ | |
81 | uint ch_s_estat; /* Realport ELAST */ | |
82 | uint ch_s_cflag; /* Realport CFLAG */ | |
83 | uint ch_s_iflag; /* Realport IFLAG */ | |
84 | uint ch_s_oflag; /* Realport OFLAG */ | |
85 | uint ch_s_xflag; /* Realport XFLAG */ | |
86 | uint ch_s_mstat; /* Realport MLAST */ | |
87 | }; | |
88 | ||
89 | #define DIGI_GETCHAN (('d'<<8) | 248) /* get channel info */ | |
90 | ||
91 | ||
92 | struct digi_vpd { | |
93 | int vpd_len; | |
94 | char vpd_data[VPDSIZE]; | |
95 | }; | |
96 | ||
97 | #define DIGI_GETVPD (('d'<<8) | 246) /* get VPD info */ | |
98 | ||
99 | ||
100 | struct digi_debug { | |
101 | int onoff; | |
102 | int port; | |
103 | }; | |
104 | ||
105 | #define DIGI_SETDEBUG (('d'<<8) | 247) /* set debug info */ | |
106 | ||
107 | ||
0b52b749 BP |
108 | /* |
109 | * dgrp_dpa_open -- open the DPA device for a particular PortServer | |
110 | */ | |
111 | static int dgrp_dpa_open(struct inode *inode, struct file *file) | |
112 | { | |
113 | struct nd_struct *nd; | |
114 | int rtn = 0; | |
115 | ||
0b52b749 BP |
116 | rtn = try_module_get(THIS_MODULE); |
117 | if (!rtn) | |
118 | return -ENXIO; | |
119 | ||
120 | rtn = 0; | |
121 | ||
122 | if (!capable(CAP_SYS_ADMIN)) { | |
123 | rtn = -EPERM; | |
124 | goto done; | |
125 | } | |
126 | ||
127 | /* | |
128 | * Make sure that the "private_data" field hasn't already been used. | |
129 | */ | |
130 | if (file->private_data) { | |
131 | rtn = -EINVAL; | |
132 | goto done; | |
133 | } | |
134 | ||
135 | /* | |
136 | * Get the node pointer, and fail if it doesn't exist. | |
137 | */ | |
d9dda78b | 138 | nd = PDE_DATA(inode); |
0b52b749 BP |
139 | if (!nd) { |
140 | rtn = -ENXIO; | |
141 | goto done; | |
142 | } | |
143 | ||
144 | file->private_data = (void *) nd; | |
145 | ||
146 | /* | |
147 | * Allocate the DPA buffer. | |
148 | */ | |
149 | ||
150 | if (nd->nd_dpa_buf) { | |
151 | rtn = -EBUSY; | |
152 | } else { | |
153 | nd->nd_dpa_buf = kmalloc(DPA_MAX, GFP_KERNEL); | |
154 | ||
155 | if (!nd->nd_dpa_buf) { | |
156 | rtn = -ENOMEM; | |
157 | } else { | |
158 | nd->nd_dpa_out = 0; | |
159 | nd->nd_dpa_in = 0; | |
160 | nd->nd_dpa_lbolt = jiffies; | |
161 | } | |
162 | } | |
163 | ||
164 | done: | |
165 | ||
166 | if (rtn) | |
167 | module_put(THIS_MODULE); | |
168 | return rtn; | |
169 | } | |
170 | ||
171 | /* | |
172 | * dgrp_dpa_release -- close the DPA device for a particular PortServer | |
173 | */ | |
174 | static int dgrp_dpa_release(struct inode *inode, struct file *file) | |
175 | { | |
176 | struct nd_struct *nd; | |
177 | u8 *buf; | |
178 | unsigned long lock_flags; | |
179 | ||
180 | /* | |
181 | * Get the node pointer, and quit if it doesn't exist. | |
182 | */ | |
183 | nd = (struct nd_struct *)(file->private_data); | |
184 | if (!nd) | |
185 | goto done; | |
186 | ||
187 | /* | |
188 | * Free the dpa buffer. | |
189 | */ | |
190 | ||
191 | spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags); | |
192 | ||
193 | buf = nd->nd_dpa_buf; | |
194 | ||
195 | nd->nd_dpa_buf = NULL; | |
196 | nd->nd_dpa_out = nd->nd_dpa_in; | |
197 | ||
198 | /* | |
199 | * Wakeup any thread waiting for buffer space. | |
200 | */ | |
201 | ||
202 | if (nd->nd_dpa_flag & DPA_WAIT_SPACE) { | |
203 | nd->nd_dpa_flag &= ~DPA_WAIT_SPACE; | |
204 | wake_up_interruptible(&nd->nd_dpa_wqueue); | |
205 | } | |
206 | ||
207 | spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags); | |
208 | ||
209 | kfree(buf); | |
210 | ||
211 | done: | |
212 | module_put(THIS_MODULE); | |
213 | file->private_data = NULL; | |
214 | return 0; | |
215 | } | |
216 | ||
217 | /* | |
218 | * dgrp_dpa_read | |
219 | * | |
220 | * Copy data from the monitoring buffer to the user, freeing space | |
221 | * in the monitoring buffer for more messages | |
222 | */ | |
223 | static ssize_t dgrp_dpa_read(struct file *file, char __user *buf, size_t count, | |
224 | loff_t *ppos) | |
225 | { | |
226 | struct nd_struct *nd; | |
227 | int n; | |
228 | int r; | |
229 | int offset = 0; | |
230 | int res = 0; | |
231 | ssize_t rtn; | |
232 | unsigned long lock_flags; | |
233 | ||
234 | /* | |
235 | * Get the node pointer, and quit if it doesn't exist. | |
236 | */ | |
237 | nd = (struct nd_struct *)(file->private_data); | |
238 | if (!nd) | |
239 | return -ENXIO; | |
240 | ||
241 | /* | |
242 | * Wait for some data to appear in the buffer. | |
243 | */ | |
244 | ||
245 | spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags); | |
246 | ||
247 | for (;;) { | |
248 | n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK; | |
249 | ||
250 | if (n != 0) | |
251 | break; | |
252 | ||
253 | nd->nd_dpa_flag |= DPA_WAIT_DATA; | |
254 | ||
255 | spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags); | |
256 | ||
257 | /* | |
258 | * Go to sleep waiting until the condition becomes true. | |
259 | */ | |
260 | rtn = wait_event_interruptible(nd->nd_dpa_wqueue, | |
261 | ((nd->nd_dpa_flag & DPA_WAIT_DATA) == 0)); | |
262 | ||
263 | if (rtn) | |
264 | return rtn; | |
265 | ||
266 | spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags); | |
267 | } | |
268 | ||
269 | /* | |
270 | * Read whatever is there. | |
271 | */ | |
272 | ||
273 | if (n > count) | |
274 | n = count; | |
275 | ||
276 | res = n; | |
277 | ||
278 | r = DPA_MAX - nd->nd_dpa_out; | |
279 | ||
280 | if (r <= n) { | |
281 | ||
282 | spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags); | |
283 | rtn = copy_to_user((void __user *)buf, | |
284 | nd->nd_dpa_buf + nd->nd_dpa_out, r); | |
285 | spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags); | |
286 | ||
287 | if (rtn) { | |
288 | rtn = -EFAULT; | |
289 | goto done; | |
290 | } | |
291 | ||
292 | nd->nd_dpa_out = 0; | |
293 | n -= r; | |
294 | offset = r; | |
295 | } | |
296 | ||
297 | spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags); | |
298 | rtn = copy_to_user((void __user *)buf + offset, | |
299 | nd->nd_dpa_buf + nd->nd_dpa_out, n); | |
300 | spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags); | |
301 | ||
302 | if (rtn) { | |
303 | rtn = -EFAULT; | |
304 | goto done; | |
305 | } | |
306 | ||
307 | nd->nd_dpa_out += n; | |
308 | ||
309 | *ppos += res; | |
310 | ||
311 | rtn = res; | |
312 | ||
313 | /* | |
314 | * Wakeup any thread waiting for buffer space. | |
315 | */ | |
316 | ||
317 | n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK; | |
318 | ||
319 | if (nd->nd_dpa_flag & DPA_WAIT_SPACE && | |
320 | (DPA_MAX - n) > DPA_HIGH_WATER) { | |
321 | nd->nd_dpa_flag &= ~DPA_WAIT_SPACE; | |
322 | wake_up_interruptible(&nd->nd_dpa_wqueue); | |
323 | } | |
324 | ||
325 | done: | |
326 | spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags); | |
327 | return rtn; | |
328 | } | |
329 | ||
330 | static unsigned int dgrp_dpa_select(struct file *file, | |
331 | struct poll_table_struct *table) | |
332 | { | |
333 | unsigned int retval = 0; | |
334 | struct nd_struct *nd = file->private_data; | |
335 | ||
336 | if (nd->nd_dpa_out != nd->nd_dpa_in) | |
337 | retval |= POLLIN | POLLRDNORM; /* Conditionally readable */ | |
338 | ||
339 | retval |= POLLOUT | POLLWRNORM; /* Always writeable */ | |
340 | ||
341 | return retval; | |
342 | } | |
343 | ||
344 | static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd, | |
345 | unsigned long arg) | |
346 | { | |
347 | ||
348 | struct nd_struct *nd; | |
349 | struct digi_chan getchan; | |
350 | struct digi_node getnode; | |
351 | struct ch_struct *ch; | |
352 | struct digi_debug setdebug; | |
353 | struct digi_vpd vpd; | |
354 | unsigned int port; | |
355 | void __user *uarg = (void __user *) arg; | |
356 | ||
357 | nd = file->private_data; | |
358 | ||
359 | switch (cmd) { | |
360 | case DIGI_GETCHAN: | |
361 | if (copy_from_user(&getchan, uarg, sizeof(struct digi_chan))) | |
362 | return -EFAULT; | |
363 | ||
364 | port = getchan.ch_port; | |
365 | ||
720a9bec | 366 | if (port > nd->nd_chan_count) |
0b52b749 BP |
367 | return -EINVAL; |
368 | ||
369 | ch = nd->nd_chan + port; | |
370 | ||
371 | getchan.ch_open = (ch->ch_open_count > 0) ? 1 : 0; | |
372 | getchan.ch_txcount = ch->ch_txcount; | |
373 | getchan.ch_rxcount = ch->ch_rxcount; | |
374 | getchan.ch_s_brate = ch->ch_s_brate; | |
375 | getchan.ch_s_estat = ch->ch_s_elast; | |
376 | getchan.ch_s_cflag = ch->ch_s_cflag; | |
377 | getchan.ch_s_iflag = ch->ch_s_iflag; | |
378 | getchan.ch_s_oflag = ch->ch_s_oflag; | |
379 | getchan.ch_s_xflag = ch->ch_s_xflag; | |
380 | getchan.ch_s_mstat = ch->ch_s_mlast; | |
381 | ||
382 | if (copy_to_user(uarg, &getchan, sizeof(struct digi_chan))) | |
383 | return -EFAULT; | |
384 | break; | |
385 | ||
386 | ||
387 | case DIGI_GETNODE: | |
388 | getnode.nd_state = (nd->nd_state & NS_READY) ? 1 : 0; | |
389 | getnode.nd_chan_count = nd->nd_chan_count; | |
390 | getnode.nd_tx_byte = nd->nd_tx_byte; | |
391 | getnode.nd_rx_byte = nd->nd_rx_byte; | |
392 | ||
393 | memset(&getnode.nd_ps_desc, 0, MAX_DESC_LEN); | |
394 | strncpy(getnode.nd_ps_desc, nd->nd_ps_desc, MAX_DESC_LEN); | |
395 | ||
396 | if (copy_to_user(uarg, &getnode, sizeof(struct digi_node))) | |
397 | return -EFAULT; | |
398 | break; | |
399 | ||
400 | ||
401 | case DIGI_SETDEBUG: | |
402 | if (copy_from_user(&setdebug, uarg, sizeof(struct digi_debug))) | |
403 | return -EFAULT; | |
404 | ||
405 | nd->nd_dpa_debug = setdebug.onoff; | |
406 | nd->nd_dpa_port = setdebug.port; | |
407 | break; | |
408 | ||
409 | ||
410 | case DIGI_GETVPD: | |
411 | if (nd->nd_vpd_len > 0) { | |
412 | vpd.vpd_len = nd->nd_vpd_len; | |
413 | memcpy(&vpd.vpd_data, &nd->nd_vpd, nd->nd_vpd_len); | |
414 | } else { | |
415 | vpd.vpd_len = 0; | |
416 | } | |
417 | ||
418 | if (copy_to_user(uarg, &vpd, sizeof(struct digi_vpd))) | |
419 | return -EFAULT; | |
420 | break; | |
421 | } | |
422 | ||
423 | return 0; | |
424 | } | |
425 | ||
426 | /** | |
427 | * dgrp_dpa() -- send data to the device monitor queue | |
428 | * @nd: pointer to a node structure | |
429 | * @buf: buffer of data to copy to the monitoring buffer | |
430 | * @len: number of bytes to transfer to the buffer | |
431 | * | |
432 | * Called by the net device routines to send data to the device | |
433 | * monitor queue. If the device monitor buffer is too full to | |
434 | * accept the data, it waits until the buffer is ready. | |
435 | */ | |
436 | static void dgrp_dpa(struct nd_struct *nd, u8 *buf, int nbuf) | |
437 | { | |
438 | int n; | |
439 | int r; | |
440 | unsigned long lock_flags; | |
441 | ||
442 | /* | |
443 | * Grab DPA lock. | |
444 | */ | |
445 | spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags); | |
446 | ||
447 | /* | |
448 | * Loop while data remains. | |
449 | */ | |
450 | while (nbuf > 0 && nd->nd_dpa_buf != NULL) { | |
451 | ||
452 | n = (nd->nd_dpa_out - nd->nd_dpa_in - 1) & DPA_MASK; | |
453 | ||
454 | /* | |
455 | * Enforce flow control on the DPA device. | |
456 | */ | |
457 | if (n < (DPA_MAX - DPA_HIGH_WATER)) | |
458 | nd->nd_dpa_flag |= DPA_WAIT_SPACE; | |
459 | ||
460 | /* | |
461 | * This should never happen, as the flow control above | |
462 | * should have stopped things before they got to this point. | |
463 | */ | |
464 | if (n == 0) { | |
465 | spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags); | |
466 | return; | |
467 | } | |
468 | ||
469 | /* | |
470 | * Copy as much data as will fit. | |
471 | */ | |
472 | ||
473 | if (n > nbuf) | |
474 | n = nbuf; | |
475 | ||
476 | r = DPA_MAX - nd->nd_dpa_in; | |
477 | ||
478 | if (r <= n) { | |
479 | memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, r); | |
480 | ||
481 | n -= r; | |
482 | ||
483 | nd->nd_dpa_in = 0; | |
484 | ||
485 | buf += r; | |
486 | nbuf -= r; | |
487 | } | |
488 | ||
489 | memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, n); | |
490 | ||
491 | nd->nd_dpa_in += n; | |
492 | ||
493 | buf += n; | |
494 | nbuf -= n; | |
495 | ||
496 | if (nd->nd_dpa_in >= DPA_MAX) | |
497 | pr_info_ratelimited("%s - nd->nd_dpa_in (%i) >= DPA_MAX\n", | |
498 | __func__, nd->nd_dpa_in); | |
499 | ||
500 | /* | |
501 | * Wakeup any thread waiting for data | |
502 | */ | |
503 | if (nd->nd_dpa_flag & DPA_WAIT_DATA) { | |
504 | nd->nd_dpa_flag &= ~DPA_WAIT_DATA; | |
505 | wake_up_interruptible(&nd->nd_dpa_wqueue); | |
506 | } | |
507 | } | |
508 | ||
509 | /* | |
510 | * Release the DPA lock. | |
511 | */ | |
512 | spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags); | |
513 | } | |
514 | ||
515 | /** | |
516 | * dgrp_monitor_data() -- builds a DPA data packet | |
517 | * @nd: pointer to a node structure | |
518 | * @type: type of message to be logged in the DPA buffer | |
519 | * @buf: buffer of data to be logged in the DPA buffer | |
520 | * @size -- number of bytes in the "buf" buffer | |
521 | */ | |
522 | void dgrp_dpa_data(struct nd_struct *nd, int type, u8 *buf, int size) | |
523 | { | |
524 | u8 header[5]; | |
525 | ||
526 | header[0] = type; | |
527 | ||
528 | put_unaligned_be32(size, header + 1); | |
529 | ||
530 | dgrp_dpa(nd, header, sizeof(header)); | |
531 | dgrp_dpa(nd, buf, size); | |
532 | } |