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