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