diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/n_hdlc.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/n_hdlc.c')
-rw-r--r-- | drivers/char/n_hdlc.c | 978 |
1 files changed, 978 insertions, 0 deletions
diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c new file mode 100644 index 000000000000..b3dbff1cf967 --- /dev/null +++ b/drivers/char/n_hdlc.c | |||
@@ -0,0 +1,978 @@ | |||
1 | /* generic HDLC line discipline for Linux | ||
2 | * | ||
3 | * Written by Paul Fulghum paulkf@microgate.com | ||
4 | * for Microgate Corporation | ||
5 | * | ||
6 | * Microgate and SyncLink are registered trademarks of Microgate Corporation | ||
7 | * | ||
8 | * Adapted from ppp.c, written by Michael Callahan <callahan@maths.ox.ac.uk>, | ||
9 | * Al Longyear <longyear@netcom.com>, | ||
10 | * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> | ||
11 | * | ||
12 | * Original release 01/11/99 | ||
13 | * $Id: n_hdlc.c,v 4.8 2003/05/06 21:18:51 paulkf Exp $ | ||
14 | * | ||
15 | * This code is released under the GNU General Public License (GPL) | ||
16 | * | ||
17 | * This module implements the tty line discipline N_HDLC for use with | ||
18 | * tty device drivers that support bit-synchronous HDLC communications. | ||
19 | * | ||
20 | * All HDLC data is frame oriented which means: | ||
21 | * | ||
22 | * 1. tty write calls represent one complete transmit frame of data | ||
23 | * The device driver should accept the complete frame or none of | ||
24 | * the frame (busy) in the write method. Each write call should have | ||
25 | * a byte count in the range of 2-65535 bytes (2 is min HDLC frame | ||
26 | * with 1 addr byte and 1 ctrl byte). The max byte count of 65535 | ||
27 | * should include any crc bytes required. For example, when using | ||
28 | * CCITT CRC32, 4 crc bytes are required, so the maximum size frame | ||
29 | * the application may transmit is limited to 65531 bytes. For CCITT | ||
30 | * CRC16, the maximum application frame size would be 65533. | ||
31 | * | ||
32 | * | ||
33 | * 2. receive callbacks from the device driver represents | ||
34 | * one received frame. The device driver should bypass | ||
35 | * the tty flip buffer and call the line discipline receive | ||
36 | * callback directly to avoid fragmenting or concatenating | ||
37 | * multiple frames into a single receive callback. | ||
38 | * | ||
39 | * The HDLC line discipline queues the receive frames in separate | ||
40 | * buffers so complete receive frames can be returned by the | ||
41 | * tty read calls. | ||
42 | * | ||
43 | * 3. tty read calls returns an entire frame of data or nothing. | ||
44 | * | ||
45 | * 4. all send and receive data is considered raw. No processing | ||
46 | * or translation is performed by the line discipline, regardless | ||
47 | * of the tty flags | ||
48 | * | ||
49 | * 5. When line discipline is queried for the amount of receive | ||
50 | * data available (FIOC), 0 is returned if no data available, | ||
51 | * otherwise the count of the next available frame is returned. | ||
52 | * (instead of the sum of all received frame counts). | ||
53 | * | ||
54 | * These conventions allow the standard tty programming interface | ||
55 | * to be used for synchronous HDLC applications when used with | ||
56 | * this line discipline (or another line discipline that is frame | ||
57 | * oriented such as N_PPP). | ||
58 | * | ||
59 | * The SyncLink driver (synclink.c) implements both asynchronous | ||
60 | * (using standard line discipline N_TTY) and synchronous HDLC | ||
61 | * (using N_HDLC) communications, with the latter using the above | ||
62 | * conventions. | ||
63 | * | ||
64 | * This implementation is very basic and does not maintain | ||
65 | * any statistics. The main point is to enforce the raw data | ||
66 | * and frame orientation of HDLC communications. | ||
67 | * | ||
68 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
69 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
70 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
71 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | ||
72 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
73 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
74 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
75 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
76 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
77 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
78 | * OF THE POSSIBILITY OF SUCH DAMAGE. | ||
79 | */ | ||
80 | |||
81 | #define HDLC_MAGIC 0x239e | ||
82 | #define HDLC_VERSION "$Revision: 4.8 $" | ||
83 | |||
84 | #include <linux/config.h> | ||
85 | #include <linux/module.h> | ||
86 | #include <linux/init.h> | ||
87 | #include <linux/kernel.h> | ||
88 | #include <linux/sched.h> | ||
89 | #include <linux/types.h> | ||
90 | #include <linux/fcntl.h> | ||
91 | #include <linux/interrupt.h> | ||
92 | #include <linux/ptrace.h> | ||
93 | |||
94 | #undef VERSION | ||
95 | #define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch)) | ||
96 | |||
97 | #include <linux/poll.h> | ||
98 | #include <linux/in.h> | ||
99 | #include <linux/ioctl.h> | ||
100 | #include <linux/slab.h> | ||
101 | #include <linux/tty.h> | ||
102 | #include <linux/errno.h> | ||
103 | #include <linux/string.h> /* used in new tty drivers */ | ||
104 | #include <linux/signal.h> /* used in new tty drivers */ | ||
105 | #include <linux/if.h> | ||
106 | #include <linux/bitops.h> | ||
107 | |||
108 | #include <asm/system.h> | ||
109 | #include <asm/termios.h> | ||
110 | #include <asm/uaccess.h> | ||
111 | |||
112 | /* | ||
113 | * Buffers for individual HDLC frames | ||
114 | */ | ||
115 | #define MAX_HDLC_FRAME_SIZE 65535 | ||
116 | #define DEFAULT_RX_BUF_COUNT 10 | ||
117 | #define MAX_RX_BUF_COUNT 60 | ||
118 | #define DEFAULT_TX_BUF_COUNT 1 | ||
119 | |||
120 | struct n_hdlc_buf { | ||
121 | struct n_hdlc_buf *link; | ||
122 | int count; | ||
123 | char buf[1]; | ||
124 | }; | ||
125 | |||
126 | #define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe) | ||
127 | |||
128 | struct n_hdlc_buf_list { | ||
129 | struct n_hdlc_buf *head; | ||
130 | struct n_hdlc_buf *tail; | ||
131 | int count; | ||
132 | spinlock_t spinlock; | ||
133 | }; | ||
134 | |||
135 | /** | ||
136 | * struct n_hdlc - per device instance data structure | ||
137 | * @magic - magic value for structure | ||
138 | * @flags - miscellaneous control flags | ||
139 | * @tty - ptr to TTY structure | ||
140 | * @backup_tty - TTY to use if tty gets closed | ||
141 | * @tbusy - reentrancy flag for tx wakeup code | ||
142 | * @woke_up - FIXME: describe this field | ||
143 | * @tbuf - currently transmitting tx buffer | ||
144 | * @tx_buf_list - list of pending transmit frame buffers | ||
145 | * @rx_buf_list - list of received frame buffers | ||
146 | * @tx_free_buf_list - list unused transmit frame buffers | ||
147 | * @rx_free_buf_list - list unused received frame buffers | ||
148 | */ | ||
149 | struct n_hdlc { | ||
150 | int magic; | ||
151 | __u32 flags; | ||
152 | struct tty_struct *tty; | ||
153 | struct tty_struct *backup_tty; | ||
154 | int tbusy; | ||
155 | int woke_up; | ||
156 | struct n_hdlc_buf *tbuf; | ||
157 | struct n_hdlc_buf_list tx_buf_list; | ||
158 | struct n_hdlc_buf_list rx_buf_list; | ||
159 | struct n_hdlc_buf_list tx_free_buf_list; | ||
160 | struct n_hdlc_buf_list rx_free_buf_list; | ||
161 | }; | ||
162 | |||
163 | /* | ||
164 | * HDLC buffer list manipulation functions | ||
165 | */ | ||
166 | static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list); | ||
167 | static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, | ||
168 | struct n_hdlc_buf *buf); | ||
169 | static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); | ||
170 | |||
171 | /* Local functions */ | ||
172 | |||
173 | static struct n_hdlc *n_hdlc_alloc (void); | ||
174 | |||
175 | /* debug level can be set by insmod for debugging purposes */ | ||
176 | #define DEBUG_LEVEL_INFO 1 | ||
177 | static int debuglevel; | ||
178 | |||
179 | /* max frame size for memory allocations */ | ||
180 | static int maxframe = 4096; | ||
181 | |||
182 | /* TTY callbacks */ | ||
183 | |||
184 | static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, | ||
185 | __u8 __user *buf, size_t nr); | ||
186 | static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, | ||
187 | const unsigned char *buf, size_t nr); | ||
188 | static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, | ||
189 | unsigned int cmd, unsigned long arg); | ||
190 | static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, | ||
191 | poll_table *wait); | ||
192 | static int n_hdlc_tty_open(struct tty_struct *tty); | ||
193 | static void n_hdlc_tty_close(struct tty_struct *tty); | ||
194 | static int n_hdlc_tty_room(struct tty_struct *tty); | ||
195 | static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp, | ||
196 | char *fp, int count); | ||
197 | static void n_hdlc_tty_wakeup(struct tty_struct *tty); | ||
198 | |||
199 | #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) | ||
200 | |||
201 | #define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data)) | ||
202 | #define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty) | ||
203 | |||
204 | static struct tty_ldisc n_hdlc_ldisc = { | ||
205 | .owner = THIS_MODULE, | ||
206 | .magic = TTY_LDISC_MAGIC, | ||
207 | .name = "hdlc", | ||
208 | .open = n_hdlc_tty_open, | ||
209 | .close = n_hdlc_tty_close, | ||
210 | .read = n_hdlc_tty_read, | ||
211 | .write = n_hdlc_tty_write, | ||
212 | .ioctl = n_hdlc_tty_ioctl, | ||
213 | .poll = n_hdlc_tty_poll, | ||
214 | .receive_buf = n_hdlc_tty_receive, | ||
215 | .receive_room = n_hdlc_tty_room, | ||
216 | .write_wakeup = n_hdlc_tty_wakeup, | ||
217 | }; | ||
218 | |||
219 | /** | ||
220 | * n_hdlc_release - release an n_hdlc per device line discipline info structure | ||
221 | * @n_hdlc - per device line discipline info structure | ||
222 | */ | ||
223 | static void n_hdlc_release(struct n_hdlc *n_hdlc) | ||
224 | { | ||
225 | struct tty_struct *tty = n_hdlc2tty (n_hdlc); | ||
226 | struct n_hdlc_buf *buf; | ||
227 | |||
228 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
229 | printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__); | ||
230 | |||
231 | /* Ensure that the n_hdlcd process is not hanging on select()/poll() */ | ||
232 | wake_up_interruptible (&tty->read_wait); | ||
233 | wake_up_interruptible (&tty->write_wait); | ||
234 | |||
235 | if (tty != NULL && tty->disc_data == n_hdlc) | ||
236 | tty->disc_data = NULL; /* Break the tty->n_hdlc link */ | ||
237 | |||
238 | /* Release transmit and receive buffers */ | ||
239 | for(;;) { | ||
240 | buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); | ||
241 | if (buf) { | ||
242 | kfree(buf); | ||
243 | } else | ||
244 | break; | ||
245 | } | ||
246 | for(;;) { | ||
247 | buf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); | ||
248 | if (buf) { | ||
249 | kfree(buf); | ||
250 | } else | ||
251 | break; | ||
252 | } | ||
253 | for(;;) { | ||
254 | buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); | ||
255 | if (buf) { | ||
256 | kfree(buf); | ||
257 | } else | ||
258 | break; | ||
259 | } | ||
260 | for(;;) { | ||
261 | buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); | ||
262 | if (buf) { | ||
263 | kfree(buf); | ||
264 | } else | ||
265 | break; | ||
266 | } | ||
267 | if (n_hdlc->tbuf) | ||
268 | kfree(n_hdlc->tbuf); | ||
269 | kfree(n_hdlc); | ||
270 | |||
271 | } /* end of n_hdlc_release() */ | ||
272 | |||
273 | /** | ||
274 | * n_hdlc_tty_close - line discipline close | ||
275 | * @tty - pointer to tty info structure | ||
276 | * | ||
277 | * Called when the line discipline is changed to something | ||
278 | * else, the tty is closed, or the tty detects a hangup. | ||
279 | */ | ||
280 | static void n_hdlc_tty_close(struct tty_struct *tty) | ||
281 | { | ||
282 | struct n_hdlc *n_hdlc = tty2n_hdlc (tty); | ||
283 | |||
284 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
285 | printk("%s(%d)n_hdlc_tty_close() called\n",__FILE__,__LINE__); | ||
286 | |||
287 | if (n_hdlc != NULL) { | ||
288 | if (n_hdlc->magic != HDLC_MAGIC) { | ||
289 | printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n"); | ||
290 | return; | ||
291 | } | ||
292 | #if defined(TTY_NO_WRITE_SPLIT) | ||
293 | clear_bit(TTY_NO_WRITE_SPLIT,&tty->flags); | ||
294 | #endif | ||
295 | tty->disc_data = NULL; | ||
296 | if (tty == n_hdlc->backup_tty) | ||
297 | n_hdlc->backup_tty = NULL; | ||
298 | if (tty != n_hdlc->tty) | ||
299 | return; | ||
300 | if (n_hdlc->backup_tty) { | ||
301 | n_hdlc->tty = n_hdlc->backup_tty; | ||
302 | } else { | ||
303 | n_hdlc_release (n_hdlc); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
308 | printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__); | ||
309 | |||
310 | } /* end of n_hdlc_tty_close() */ | ||
311 | |||
312 | /** | ||
313 | * n_hdlc_tty_open - called when line discipline changed to n_hdlc | ||
314 | * @tty - pointer to tty info structure | ||
315 | * | ||
316 | * Returns 0 if success, otherwise error code | ||
317 | */ | ||
318 | static int n_hdlc_tty_open (struct tty_struct *tty) | ||
319 | { | ||
320 | struct n_hdlc *n_hdlc = tty2n_hdlc (tty); | ||
321 | |||
322 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
323 | printk("%s(%d)n_hdlc_tty_open() called (device=%s)\n", | ||
324 | __FILE__,__LINE__, | ||
325 | tty->name); | ||
326 | |||
327 | /* There should not be an existing table for this slot. */ | ||
328 | if (n_hdlc) { | ||
329 | printk (KERN_ERR"n_hdlc_tty_open:tty already associated!\n" ); | ||
330 | return -EEXIST; | ||
331 | } | ||
332 | |||
333 | n_hdlc = n_hdlc_alloc(); | ||
334 | if (!n_hdlc) { | ||
335 | printk (KERN_ERR "n_hdlc_alloc failed\n"); | ||
336 | return -ENFILE; | ||
337 | } | ||
338 | |||
339 | tty->disc_data = n_hdlc; | ||
340 | n_hdlc->tty = tty; | ||
341 | |||
342 | #if defined(TTY_NO_WRITE_SPLIT) | ||
343 | /* change tty_io write() to not split large writes into 8K chunks */ | ||
344 | set_bit(TTY_NO_WRITE_SPLIT,&tty->flags); | ||
345 | #endif | ||
346 | |||
347 | /* Flush any pending characters in the driver and discipline. */ | ||
348 | |||
349 | if (tty->ldisc.flush_buffer) | ||
350 | tty->ldisc.flush_buffer (tty); | ||
351 | |||
352 | if (tty->driver->flush_buffer) | ||
353 | tty->driver->flush_buffer (tty); | ||
354 | |||
355 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
356 | printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__); | ||
357 | |||
358 | return 0; | ||
359 | |||
360 | } /* end of n_tty_hdlc_open() */ | ||
361 | |||
362 | /** | ||
363 | * n_hdlc_send_frames - send frames on pending send buffer list | ||
364 | * @n_hdlc - pointer to ldisc instance data | ||
365 | * @tty - pointer to tty instance data | ||
366 | * | ||
367 | * Send frames on pending send buffer list until the driver does not accept a | ||
368 | * frame (busy) this function is called after adding a frame to the send buffer | ||
369 | * list and by the tty wakeup callback. | ||
370 | */ | ||
371 | static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) | ||
372 | { | ||
373 | register int actual; | ||
374 | unsigned long flags; | ||
375 | struct n_hdlc_buf *tbuf; | ||
376 | |||
377 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
378 | printk("%s(%d)n_hdlc_send_frames() called\n",__FILE__,__LINE__); | ||
379 | check_again: | ||
380 | |||
381 | spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); | ||
382 | if (n_hdlc->tbusy) { | ||
383 | n_hdlc->woke_up = 1; | ||
384 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); | ||
385 | return; | ||
386 | } | ||
387 | n_hdlc->tbusy = 1; | ||
388 | n_hdlc->woke_up = 0; | ||
389 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); | ||
390 | |||
391 | /* get current transmit buffer or get new transmit */ | ||
392 | /* buffer from list of pending transmit buffers */ | ||
393 | |||
394 | tbuf = n_hdlc->tbuf; | ||
395 | if (!tbuf) | ||
396 | tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); | ||
397 | |||
398 | while (tbuf) { | ||
399 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
400 | printk("%s(%d)sending frame %p, count=%d\n", | ||
401 | __FILE__,__LINE__,tbuf,tbuf->count); | ||
402 | |||
403 | /* Send the next block of data to device */ | ||
404 | tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); | ||
405 | actual = tty->driver->write(tty, tbuf->buf, tbuf->count); | ||
406 | |||
407 | /* if transmit error, throw frame away by */ | ||
408 | /* pretending it was accepted by driver */ | ||
409 | if (actual < 0) | ||
410 | actual = tbuf->count; | ||
411 | |||
412 | if (actual == tbuf->count) { | ||
413 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
414 | printk("%s(%d)frame %p completed\n", | ||
415 | __FILE__,__LINE__,tbuf); | ||
416 | |||
417 | /* free current transmit buffer */ | ||
418 | n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); | ||
419 | |||
420 | /* this tx buffer is done */ | ||
421 | n_hdlc->tbuf = NULL; | ||
422 | |||
423 | /* wait up sleeping writers */ | ||
424 | wake_up_interruptible(&tty->write_wait); | ||
425 | |||
426 | /* get next pending transmit buffer */ | ||
427 | tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); | ||
428 | } else { | ||
429 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
430 | printk("%s(%d)frame %p pending\n", | ||
431 | __FILE__,__LINE__,tbuf); | ||
432 | |||
433 | /* buffer not accepted by driver */ | ||
434 | /* set this buffer as pending buffer */ | ||
435 | n_hdlc->tbuf = tbuf; | ||
436 | break; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | if (!tbuf) | ||
441 | tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); | ||
442 | |||
443 | /* Clear the re-entry flag */ | ||
444 | spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); | ||
445 | n_hdlc->tbusy = 0; | ||
446 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); | ||
447 | |||
448 | if (n_hdlc->woke_up) | ||
449 | goto check_again; | ||
450 | |||
451 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
452 | printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__); | ||
453 | |||
454 | } /* end of n_hdlc_send_frames() */ | ||
455 | |||
456 | /** | ||
457 | * n_hdlc_tty_wakeup - Callback for transmit wakeup | ||
458 | * @tty - pointer to associated tty instance data | ||
459 | * | ||
460 | * Called when low level device driver can accept more send data. | ||
461 | */ | ||
462 | static void n_hdlc_tty_wakeup(struct tty_struct *tty) | ||
463 | { | ||
464 | struct n_hdlc *n_hdlc = tty2n_hdlc(tty); | ||
465 | |||
466 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
467 | printk("%s(%d)n_hdlc_tty_wakeup() called\n",__FILE__,__LINE__); | ||
468 | |||
469 | if (!n_hdlc) | ||
470 | return; | ||
471 | |||
472 | if (tty != n_hdlc->tty) { | ||
473 | tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); | ||
474 | return; | ||
475 | } | ||
476 | |||
477 | n_hdlc_send_frames (n_hdlc, tty); | ||
478 | |||
479 | } /* end of n_hdlc_tty_wakeup() */ | ||
480 | |||
481 | /** | ||
482 | * n_hdlc_tty_room - Return the amount of space left in the receiver's buffer | ||
483 | * @tty - pointer to associated tty instance data | ||
484 | * | ||
485 | * Callback function from tty driver. Return the amount of space left in the | ||
486 | * receiver's buffer to decide if remote transmitter is to be throttled. | ||
487 | */ | ||
488 | static int n_hdlc_tty_room(struct tty_struct *tty) | ||
489 | { | ||
490 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
491 | printk("%s(%d)n_hdlc_tty_room() called\n",__FILE__,__LINE__); | ||
492 | /* always return a larger number to prevent */ | ||
493 | /* throttling of remote transmitter. */ | ||
494 | return 65536; | ||
495 | } /* end of n_hdlc_tty_root() */ | ||
496 | |||
497 | /** | ||
498 | * n_hdlc_tty_receive - Called by tty driver when receive data is available | ||
499 | * @tty - pointer to tty instance data | ||
500 | * @data - pointer to received data | ||
501 | * @flags - pointer to flags for data | ||
502 | * @count - count of received data in bytes | ||
503 | * | ||
504 | * Called by tty low level driver when receive data is available. Data is | ||
505 | * interpreted as one HDLC frame. | ||
506 | */ | ||
507 | static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, | ||
508 | char *flags, int count) | ||
509 | { | ||
510 | register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); | ||
511 | register struct n_hdlc_buf *buf; | ||
512 | |||
513 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
514 | printk("%s(%d)n_hdlc_tty_receive() called count=%d\n", | ||
515 | __FILE__,__LINE__, count); | ||
516 | |||
517 | /* This can happen if stuff comes in on the backup tty */ | ||
518 | if (n_hdlc == 0 || tty != n_hdlc->tty) | ||
519 | return; | ||
520 | |||
521 | /* verify line is using HDLC discipline */ | ||
522 | if (n_hdlc->magic != HDLC_MAGIC) { | ||
523 | printk("%s(%d) line not using HDLC discipline\n", | ||
524 | __FILE__,__LINE__); | ||
525 | return; | ||
526 | } | ||
527 | |||
528 | if ( count>maxframe ) { | ||
529 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
530 | printk("%s(%d) rx count>maxframesize, data discarded\n", | ||
531 | __FILE__,__LINE__); | ||
532 | return; | ||
533 | } | ||
534 | |||
535 | /* get a free HDLC buffer */ | ||
536 | buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); | ||
537 | if (!buf) { | ||
538 | /* no buffers in free list, attempt to allocate another rx buffer */ | ||
539 | /* unless the maximum count has been reached */ | ||
540 | if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT) | ||
541 | buf = kmalloc(N_HDLC_BUF_SIZE, GFP_ATOMIC); | ||
542 | } | ||
543 | |||
544 | if (!buf) { | ||
545 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
546 | printk("%s(%d) no more rx buffers, data discarded\n", | ||
547 | __FILE__,__LINE__); | ||
548 | return; | ||
549 | } | ||
550 | |||
551 | /* copy received data to HDLC buffer */ | ||
552 | memcpy(buf->buf,data,count); | ||
553 | buf->count=count; | ||
554 | |||
555 | /* add HDLC buffer to list of received frames */ | ||
556 | n_hdlc_buf_put(&n_hdlc->rx_buf_list, buf); | ||
557 | |||
558 | /* wake up any blocked reads and perform async signalling */ | ||
559 | wake_up_interruptible (&tty->read_wait); | ||
560 | if (n_hdlc->tty->fasync != NULL) | ||
561 | kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); | ||
562 | |||
563 | } /* end of n_hdlc_tty_receive() */ | ||
564 | |||
565 | /** | ||
566 | * n_hdlc_tty_read - Called to retreive one frame of data (if available) | ||
567 | * @tty - pointer to tty instance data | ||
568 | * @file - pointer to open file object | ||
569 | * @buf - pointer to returned data buffer | ||
570 | * @nr - size of returned data buffer | ||
571 | * | ||
572 | * Returns the number of bytes returned or error code. | ||
573 | */ | ||
574 | static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, | ||
575 | __u8 __user *buf, size_t nr) | ||
576 | { | ||
577 | struct n_hdlc *n_hdlc = tty2n_hdlc(tty); | ||
578 | int ret; | ||
579 | struct n_hdlc_buf *rbuf; | ||
580 | |||
581 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
582 | printk("%s(%d)n_hdlc_tty_read() called\n",__FILE__,__LINE__); | ||
583 | |||
584 | /* Validate the pointers */ | ||
585 | if (!n_hdlc) | ||
586 | return -EIO; | ||
587 | |||
588 | /* verify user access to buffer */ | ||
589 | if (!access_ok(VERIFY_WRITE, buf, nr)) { | ||
590 | printk(KERN_WARNING "%s(%d) n_hdlc_tty_read() can't verify user " | ||
591 | "buffer\n", __FILE__, __LINE__); | ||
592 | return -EFAULT; | ||
593 | } | ||
594 | |||
595 | for (;;) { | ||
596 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) | ||
597 | return -EIO; | ||
598 | |||
599 | n_hdlc = tty2n_hdlc (tty); | ||
600 | if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || | ||
601 | tty != n_hdlc->tty) | ||
602 | return 0; | ||
603 | |||
604 | rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); | ||
605 | if (rbuf) | ||
606 | break; | ||
607 | |||
608 | /* no data */ | ||
609 | if (file->f_flags & O_NONBLOCK) | ||
610 | return -EAGAIN; | ||
611 | |||
612 | interruptible_sleep_on (&tty->read_wait); | ||
613 | if (signal_pending(current)) | ||
614 | return -EINTR; | ||
615 | } | ||
616 | |||
617 | if (rbuf->count > nr) | ||
618 | /* frame too large for caller's buffer (discard frame) */ | ||
619 | ret = -EOVERFLOW; | ||
620 | else { | ||
621 | /* Copy the data to the caller's buffer */ | ||
622 | if (copy_to_user(buf, rbuf->buf, rbuf->count)) | ||
623 | ret = -EFAULT; | ||
624 | else | ||
625 | ret = rbuf->count; | ||
626 | } | ||
627 | |||
628 | /* return HDLC buffer to free list unless the free list */ | ||
629 | /* count has exceeded the default value, in which case the */ | ||
630 | /* buffer is freed back to the OS to conserve memory */ | ||
631 | if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT) | ||
632 | kfree(rbuf); | ||
633 | else | ||
634 | n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf); | ||
635 | |||
636 | return ret; | ||
637 | |||
638 | } /* end of n_hdlc_tty_read() */ | ||
639 | |||
640 | /** | ||
641 | * n_hdlc_tty_write - write a single frame of data to device | ||
642 | * @tty - pointer to associated tty device instance data | ||
643 | * @file - pointer to file object data | ||
644 | * @data - pointer to transmit data (one frame) | ||
645 | * @count - size of transmit frame in bytes | ||
646 | * | ||
647 | * Returns the number of bytes written (or error code). | ||
648 | */ | ||
649 | static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, | ||
650 | const unsigned char *data, size_t count) | ||
651 | { | ||
652 | struct n_hdlc *n_hdlc = tty2n_hdlc (tty); | ||
653 | int error = 0; | ||
654 | DECLARE_WAITQUEUE(wait, current); | ||
655 | struct n_hdlc_buf *tbuf; | ||
656 | |||
657 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
658 | printk("%s(%d)n_hdlc_tty_write() called count=%Zd\n", | ||
659 | __FILE__,__LINE__,count); | ||
660 | |||
661 | /* Verify pointers */ | ||
662 | if (!n_hdlc) | ||
663 | return -EIO; | ||
664 | |||
665 | if (n_hdlc->magic != HDLC_MAGIC) | ||
666 | return -EIO; | ||
667 | |||
668 | /* verify frame size */ | ||
669 | if (count > maxframe ) { | ||
670 | if (debuglevel & DEBUG_LEVEL_INFO) | ||
671 | printk (KERN_WARNING | ||
672 | "n_hdlc_tty_write: truncating user packet " | ||
673 | "from %lu to %d\n", (unsigned long) count, | ||
674 | maxframe ); | ||
675 | count = maxframe; | ||
676 | } | ||
677 | |||
678 | add_wait_queue(&tty->write_wait, &wait); | ||
679 | set_current_state(TASK_INTERRUPTIBLE); | ||
680 | |||
681 | /* Allocate transmit buffer */ | ||
682 | /* sleep until transmit buffer available */ | ||
683 | while (!(tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list))) { | ||
684 | schedule(); | ||
685 | |||
686 | n_hdlc = tty2n_hdlc (tty); | ||
687 | if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || | ||
688 | tty != n_hdlc->tty) { | ||
689 | printk("n_hdlc_tty_write: %p invalid after wait!\n", n_hdlc); | ||
690 | error = -EIO; | ||
691 | break; | ||
692 | } | ||
693 | |||
694 | if (signal_pending(current)) { | ||
695 | error = -EINTR; | ||
696 | break; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | set_current_state(TASK_RUNNING); | ||
701 | remove_wait_queue(&tty->write_wait, &wait); | ||
702 | |||
703 | if (!error) { | ||
704 | /* Retrieve the user's buffer */ | ||
705 | memcpy(tbuf->buf, data, count); | ||
706 | |||
707 | /* Send the data */ | ||
708 | tbuf->count = error = count; | ||
709 | n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf); | ||
710 | n_hdlc_send_frames(n_hdlc,tty); | ||
711 | } | ||
712 | |||
713 | return error; | ||
714 | |||
715 | } /* end of n_hdlc_tty_write() */ | ||
716 | |||
717 | /** | ||
718 | * n_hdlc_tty_ioctl - process IOCTL system call for the tty device. | ||
719 | * @tty - pointer to tty instance data | ||
720 | * @file - pointer to open file object for device | ||
721 | * @cmd - IOCTL command code | ||
722 | * @arg - argument for IOCTL call (cmd dependent) | ||
723 | * | ||
724 | * Returns command dependent result. | ||
725 | */ | ||
726 | static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, | ||
727 | unsigned int cmd, unsigned long arg) | ||
728 | { | ||
729 | struct n_hdlc *n_hdlc = tty2n_hdlc (tty); | ||
730 | int error = 0; | ||
731 | int count; | ||
732 | unsigned long flags; | ||
733 | |||
734 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
735 | printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", | ||
736 | __FILE__,__LINE__,cmd); | ||
737 | |||
738 | /* Verify the status of the device */ | ||
739 | if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC) | ||
740 | return -EBADF; | ||
741 | |||
742 | switch (cmd) { | ||
743 | case FIONREAD: | ||
744 | /* report count of read data available */ | ||
745 | /* in next available frame (if any) */ | ||
746 | spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); | ||
747 | if (n_hdlc->rx_buf_list.head) | ||
748 | count = n_hdlc->rx_buf_list.head->count; | ||
749 | else | ||
750 | count = 0; | ||
751 | spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); | ||
752 | error = put_user(count, (int __user *)arg); | ||
753 | break; | ||
754 | |||
755 | case TIOCOUTQ: | ||
756 | /* get the pending tx byte count in the driver */ | ||
757 | count = tty->driver->chars_in_buffer ? | ||
758 | tty->driver->chars_in_buffer(tty) : 0; | ||
759 | /* add size of next output frame in queue */ | ||
760 | spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); | ||
761 | if (n_hdlc->tx_buf_list.head) | ||
762 | count += n_hdlc->tx_buf_list.head->count; | ||
763 | spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); | ||
764 | error = put_user(count, (int __user *)arg); | ||
765 | break; | ||
766 | |||
767 | default: | ||
768 | error = n_tty_ioctl (tty, file, cmd, arg); | ||
769 | break; | ||
770 | } | ||
771 | return error; | ||
772 | |||
773 | } /* end of n_hdlc_tty_ioctl() */ | ||
774 | |||
775 | /** | ||
776 | * n_hdlc_tty_poll - TTY callback for poll system call | ||
777 | * @tty - pointer to tty instance data | ||
778 | * @filp - pointer to open file object for device | ||
779 | * @poll_table - wait queue for operations | ||
780 | * | ||
781 | * Determine which operations (read/write) will not block and return info | ||
782 | * to caller. | ||
783 | * Returns a bit mask containing info on which ops will not block. | ||
784 | */ | ||
785 | static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, | ||
786 | poll_table *wait) | ||
787 | { | ||
788 | struct n_hdlc *n_hdlc = tty2n_hdlc (tty); | ||
789 | unsigned int mask = 0; | ||
790 | |||
791 | if (debuglevel >= DEBUG_LEVEL_INFO) | ||
792 | printk("%s(%d)n_hdlc_tty_poll() called\n",__FILE__,__LINE__); | ||
793 | |||
794 | if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) { | ||
795 | /* queue current process into any wait queue that */ | ||
796 | /* may awaken in the future (read and write) */ | ||
797 | |||
798 | poll_wait(filp, &tty->read_wait, wait); | ||
799 | poll_wait(filp, &tty->write_wait, wait); | ||
800 | |||
801 | /* set bits for operations that won't block */ | ||
802 | if(n_hdlc->rx_buf_list.head) | ||
803 | mask |= POLLIN | POLLRDNORM; /* readable */ | ||
804 | if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) | ||
805 | mask |= POLLHUP; | ||
806 | if(tty_hung_up_p(filp)) | ||
807 | mask |= POLLHUP; | ||
808 | if(n_hdlc->tx_free_buf_list.head) | ||
809 | mask |= POLLOUT | POLLWRNORM; /* writable */ | ||
810 | } | ||
811 | return mask; | ||
812 | } /* end of n_hdlc_tty_poll() */ | ||
813 | |||
814 | /** | ||
815 | * n_hdlc_alloc - allocate an n_hdlc instance data structure | ||
816 | * | ||
817 | * Returns a pointer to newly created structure if success, otherwise %NULL | ||
818 | */ | ||
819 | static struct n_hdlc *n_hdlc_alloc(void) | ||
820 | { | ||
821 | struct n_hdlc_buf *buf; | ||
822 | int i; | ||
823 | struct n_hdlc *n_hdlc = kmalloc(sizeof(*n_hdlc), GFP_KERNEL); | ||
824 | |||
825 | if (!n_hdlc) | ||
826 | return NULL; | ||
827 | |||
828 | memset(n_hdlc, 0, sizeof(*n_hdlc)); | ||
829 | |||
830 | n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); | ||
831 | n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); | ||
832 | n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); | ||
833 | n_hdlc_buf_list_init(&n_hdlc->tx_buf_list); | ||
834 | |||
835 | /* allocate free rx buffer list */ | ||
836 | for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { | ||
837 | buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL); | ||
838 | if (buf) | ||
839 | n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,buf); | ||
840 | else if (debuglevel >= DEBUG_LEVEL_INFO) | ||
841 | printk("%s(%d)n_hdlc_alloc(), kalloc() failed for rx buffer %d\n",__FILE__,__LINE__, i); | ||
842 | } | ||
843 | |||
844 | /* allocate free tx buffer list */ | ||
845 | for(i=0;i<DEFAULT_TX_BUF_COUNT;i++) { | ||
846 | buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL); | ||
847 | if (buf) | ||
848 | n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,buf); | ||
849 | else if (debuglevel >= DEBUG_LEVEL_INFO) | ||
850 | printk("%s(%d)n_hdlc_alloc(), kalloc() failed for tx buffer %d\n",__FILE__,__LINE__, i); | ||
851 | } | ||
852 | |||
853 | /* Initialize the control block */ | ||
854 | n_hdlc->magic = HDLC_MAGIC; | ||
855 | n_hdlc->flags = 0; | ||
856 | |||
857 | return n_hdlc; | ||
858 | |||
859 | } /* end of n_hdlc_alloc() */ | ||
860 | |||
861 | /** | ||
862 | * n_hdlc_buf_list_init - initialize specified HDLC buffer list | ||
863 | * @list - pointer to buffer list | ||
864 | */ | ||
865 | static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list) | ||
866 | { | ||
867 | memset(list, 0, sizeof(*list)); | ||
868 | spin_lock_init(&list->spinlock); | ||
869 | } /* end of n_hdlc_buf_list_init() */ | ||
870 | |||
871 | /** | ||
872 | * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list | ||
873 | * @list - pointer to buffer list | ||
874 | * @buf - pointer to buffer | ||
875 | */ | ||
876 | static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, | ||
877 | struct n_hdlc_buf *buf) | ||
878 | { | ||
879 | unsigned long flags; | ||
880 | spin_lock_irqsave(&list->spinlock,flags); | ||
881 | |||
882 | buf->link=NULL; | ||
883 | if(list->tail) | ||
884 | list->tail->link = buf; | ||
885 | else | ||
886 | list->head = buf; | ||
887 | list->tail = buf; | ||
888 | (list->count)++; | ||
889 | |||
890 | spin_unlock_irqrestore(&list->spinlock,flags); | ||
891 | |||
892 | } /* end of n_hdlc_buf_put() */ | ||
893 | |||
894 | /** | ||
895 | * n_hdlc_buf_get - remove and return an HDLC buffer from list | ||
896 | * @list - pointer to HDLC buffer list | ||
897 | * | ||
898 | * Remove and return an HDLC buffer from the head of the specified HDLC buffer | ||
899 | * list. | ||
900 | * Returns a pointer to HDLC buffer if available, otherwise %NULL. | ||
901 | */ | ||
902 | static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list) | ||
903 | { | ||
904 | unsigned long flags; | ||
905 | struct n_hdlc_buf *buf; | ||
906 | spin_lock_irqsave(&list->spinlock,flags); | ||
907 | |||
908 | buf = list->head; | ||
909 | if (buf) { | ||
910 | list->head = buf->link; | ||
911 | (list->count)--; | ||
912 | } | ||
913 | if (!list->head) | ||
914 | list->tail = NULL; | ||
915 | |||
916 | spin_unlock_irqrestore(&list->spinlock,flags); | ||
917 | return buf; | ||
918 | |||
919 | } /* end of n_hdlc_buf_get() */ | ||
920 | |||
921 | static char hdlc_banner[] __initdata = | ||
922 | KERN_INFO "HDLC line discipline: version " HDLC_VERSION | ||
923 | ", maxframe=%u\n"; | ||
924 | static char hdlc_register_ok[] __initdata = | ||
925 | KERN_INFO "N_HDLC line discipline registered.\n"; | ||
926 | static char hdlc_register_fail[] __initdata = | ||
927 | KERN_ERR "error registering line discipline: %d\n"; | ||
928 | static char hdlc_init_fail[] __initdata = | ||
929 | KERN_INFO "N_HDLC: init failure %d\n"; | ||
930 | |||
931 | static int __init n_hdlc_init(void) | ||
932 | { | ||
933 | int status; | ||
934 | |||
935 | /* range check maxframe arg */ | ||
936 | if (maxframe < 4096) | ||
937 | maxframe = 4096; | ||
938 | else if (maxframe > 65535) | ||
939 | maxframe = 65535; | ||
940 | |||
941 | printk(hdlc_banner, maxframe); | ||
942 | |||
943 | status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc); | ||
944 | if (!status) | ||
945 | printk(hdlc_register_ok); | ||
946 | else | ||
947 | printk(hdlc_register_fail, status); | ||
948 | |||
949 | if (status) | ||
950 | printk(hdlc_init_fail, status); | ||
951 | return status; | ||
952 | |||
953 | } /* end of init_module() */ | ||
954 | |||
955 | static char hdlc_unregister_ok[] __exitdata = | ||
956 | KERN_INFO "N_HDLC: line discipline unregistered\n"; | ||
957 | static char hdlc_unregister_fail[] __exitdata = | ||
958 | KERN_ERR "N_HDLC: can't unregister line discipline (err = %d)\n"; | ||
959 | |||
960 | static void __exit n_hdlc_exit(void) | ||
961 | { | ||
962 | /* Release tty registration of line discipline */ | ||
963 | int status = tty_register_ldisc(N_HDLC, NULL); | ||
964 | |||
965 | if (status) | ||
966 | printk(hdlc_unregister_fail, status); | ||
967 | else | ||
968 | printk(hdlc_unregister_ok); | ||
969 | } | ||
970 | |||
971 | module_init(n_hdlc_init); | ||
972 | module_exit(n_hdlc_exit); | ||
973 | |||
974 | MODULE_LICENSE("GPL"); | ||
975 | MODULE_AUTHOR("Paul Fulghum paulkf@microgate.com"); | ||
976 | module_param(debuglevel, int, 0); | ||
977 | module_param(maxframe, int, 0); | ||
978 | MODULE_ALIAS_LDISC(N_HDLC); | ||