diff options
Diffstat (limited to 'net/irda/irnet/irnet_ppp.c')
-rw-r--r-- | net/irda/irnet/irnet_ppp.c | 1142 |
1 files changed, 1142 insertions, 0 deletions
diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c new file mode 100644 index 000000000000..f8f984bb9922 --- /dev/null +++ b/net/irda/irnet/irnet_ppp.c | |||
@@ -0,0 +1,1142 @@ | |||
1 | /* | ||
2 | * IrNET protocol module : Synchronous PPP over an IrDA socket. | ||
3 | * | ||
4 | * Jean II - HPL `00 - <jt@hpl.hp.com> | ||
5 | * | ||
6 | * This file implement the PPP interface and /dev/irnet character device. | ||
7 | * The PPP interface hook to the ppp_generic module, handle all our | ||
8 | * relationship to the PPP code in the kernel (and by extension to pppd), | ||
9 | * and exchange PPP frames with this module (send/receive). | ||
10 | * The /dev/irnet device is used primarily for 2 functions : | ||
11 | * 1) as a stub for pppd (the ppp daemon), so that we can appropriately | ||
12 | * generate PPP sessions (we pretend we are a tty). | ||
13 | * 2) as a control channel (write commands, read events) | ||
14 | */ | ||
15 | |||
16 | #include "irnet_ppp.h" /* Private header */ | ||
17 | /* Please put other headers in irnet.h - Thanks */ | ||
18 | |||
19 | /* Generic PPP callbacks (to call us) */ | ||
20 | static struct ppp_channel_ops irnet_ppp_ops = { | ||
21 | .start_xmit = ppp_irnet_send, | ||
22 | .ioctl = ppp_irnet_ioctl | ||
23 | }; | ||
24 | |||
25 | /************************* CONTROL CHANNEL *************************/ | ||
26 | /* | ||
27 | * When a pppd instance is not active on /dev/irnet, it acts as a control | ||
28 | * channel. | ||
29 | * Writing allow to set up the IrDA destination of the IrNET channel, | ||
30 | * and any application may be read events happening in IrNET... | ||
31 | */ | ||
32 | |||
33 | /*------------------------------------------------------------------*/ | ||
34 | /* | ||
35 | * Write is used to send a command to configure a IrNET channel | ||
36 | * before it is open by pppd. The syntax is : "command argument" | ||
37 | * Currently there is only two defined commands : | ||
38 | * o name : set the requested IrDA nickname of the IrNET peer. | ||
39 | * o addr : set the requested IrDA address of the IrNET peer. | ||
40 | * Note : the code is crude, but effective... | ||
41 | */ | ||
42 | static inline ssize_t | ||
43 | irnet_ctrl_write(irnet_socket * ap, | ||
44 | const char __user *buf, | ||
45 | size_t count) | ||
46 | { | ||
47 | char command[IRNET_MAX_COMMAND]; | ||
48 | char * start; /* Current command being processed */ | ||
49 | char * next; /* Next command to process */ | ||
50 | int length; /* Length of current command */ | ||
51 | |||
52 | DENTER(CTRL_TRACE, "(ap=0x%p, count=%Zd)\n", ap, count); | ||
53 | |||
54 | /* Check for overflow... */ | ||
55 | DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM, | ||
56 | CTRL_ERROR, "Too much data !!!\n"); | ||
57 | |||
58 | /* Get the data in the driver */ | ||
59 | if(copy_from_user(command, buf, count)) | ||
60 | { | ||
61 | DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); | ||
62 | return -EFAULT; | ||
63 | } | ||
64 | |||
65 | /* Safe terminate the string */ | ||
66 | command[count] = '\0'; | ||
67 | DEBUG(CTRL_INFO, "Command line received is ``%s'' (%Zd).\n", | ||
68 | command, count); | ||
69 | |||
70 | /* Check every commands in the command line */ | ||
71 | next = command; | ||
72 | while(next != NULL) | ||
73 | { | ||
74 | /* Look at the next command */ | ||
75 | start = next; | ||
76 | |||
77 | /* Scrap whitespaces before the command */ | ||
78 | while(isspace(*start)) | ||
79 | start++; | ||
80 | |||
81 | /* ',' is our command separator */ | ||
82 | next = strchr(start, ','); | ||
83 | if(next) | ||
84 | { | ||
85 | *next = '\0'; /* Terminate command */ | ||
86 | length = next - start; /* Length */ | ||
87 | next++; /* Skip the '\0' */ | ||
88 | } | ||
89 | else | ||
90 | length = strlen(start); | ||
91 | |||
92 | DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length); | ||
93 | |||
94 | /* Check if we recognised one of the known command | ||
95 | * We can't use "switch" with strings, so hack with "continue" */ | ||
96 | |||
97 | /* First command : name -> Requested IrDA nickname */ | ||
98 | if(!strncmp(start, "name", 4)) | ||
99 | { | ||
100 | /* Copy the name only if is included and not "any" */ | ||
101 | if((length > 5) && (strcmp(start + 5, "any"))) | ||
102 | { | ||
103 | /* Strip out trailing whitespaces */ | ||
104 | while(isspace(start[length - 1])) | ||
105 | length--; | ||
106 | |||
107 | /* Copy the name for later reuse */ | ||
108 | memcpy(ap->rname, start + 5, length - 5); | ||
109 | ap->rname[length - 5] = '\0'; | ||
110 | } | ||
111 | else | ||
112 | ap->rname[0] = '\0'; | ||
113 | DEBUG(CTRL_INFO, "Got rname = ``%s''\n", ap->rname); | ||
114 | |||
115 | /* Restart the loop */ | ||
116 | continue; | ||
117 | } | ||
118 | |||
119 | /* Second command : addr, daddr -> Requested IrDA destination address | ||
120 | * Also process : saddr -> Requested IrDA source address */ | ||
121 | if((!strncmp(start, "addr", 4)) || | ||
122 | (!strncmp(start, "daddr", 5)) || | ||
123 | (!strncmp(start, "saddr", 5))) | ||
124 | { | ||
125 | __u32 addr = DEV_ADDR_ANY; | ||
126 | |||
127 | /* Copy the address only if is included and not "any" */ | ||
128 | if((length > 5) && (strcmp(start + 5, "any"))) | ||
129 | { | ||
130 | char * begp = start + 5; | ||
131 | char * endp; | ||
132 | |||
133 | /* Scrap whitespaces before the command */ | ||
134 | while(isspace(*begp)) | ||
135 | begp++; | ||
136 | |||
137 | /* Convert argument to a number (last arg is the base) */ | ||
138 | addr = simple_strtoul(begp, &endp, 16); | ||
139 | /* Has it worked ? (endp should be start + length) */ | ||
140 | DABORT(endp <= (start + 5), -EINVAL, | ||
141 | CTRL_ERROR, "Invalid address.\n"); | ||
142 | } | ||
143 | /* Which type of address ? */ | ||
144 | if(start[0] == 's') | ||
145 | { | ||
146 | /* Save it */ | ||
147 | ap->rsaddr = addr; | ||
148 | DEBUG(CTRL_INFO, "Got rsaddr = %08x\n", ap->rsaddr); | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | /* Save it */ | ||
153 | ap->rdaddr = addr; | ||
154 | DEBUG(CTRL_INFO, "Got rdaddr = %08x\n", ap->rdaddr); | ||
155 | } | ||
156 | |||
157 | /* Restart the loop */ | ||
158 | continue; | ||
159 | } | ||
160 | |||
161 | /* Other possible command : connect N (number of retries) */ | ||
162 | |||
163 | /* No command matched -> Failed... */ | ||
164 | DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n"); | ||
165 | } | ||
166 | |||
167 | /* Success : we have parsed all commands successfully */ | ||
168 | return(count); | ||
169 | } | ||
170 | |||
171 | #ifdef INITIAL_DISCOVERY | ||
172 | /*------------------------------------------------------------------*/ | ||
173 | /* | ||
174 | * Function irnet_get_discovery_log (self) | ||
175 | * | ||
176 | * Query the content on the discovery log if not done | ||
177 | * | ||
178 | * This function query the current content of the discovery log | ||
179 | * at the startup of the event channel and save it in the internal struct. | ||
180 | */ | ||
181 | static void | ||
182 | irnet_get_discovery_log(irnet_socket * ap) | ||
183 | { | ||
184 | __u16 mask = irlmp_service_to_hint(S_LAN); | ||
185 | |||
186 | /* Ask IrLMP for the current discovery log */ | ||
187 | ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask, | ||
188 | DISCOVERY_DEFAULT_SLOTS); | ||
189 | |||
190 | /* Check if the we got some results */ | ||
191 | if(ap->discoveries == NULL) | ||
192 | ap->disco_number = -1; | ||
193 | |||
194 | DEBUG(CTRL_INFO, "Got the log (0x%p), size is %d\n", | ||
195 | ap->discoveries, ap->disco_number); | ||
196 | } | ||
197 | |||
198 | /*------------------------------------------------------------------*/ | ||
199 | /* | ||
200 | * Function irnet_read_discovery_log (self, event) | ||
201 | * | ||
202 | * Read the content on the discovery log | ||
203 | * | ||
204 | * This function dump the current content of the discovery log | ||
205 | * at the startup of the event channel. | ||
206 | * Return 1 if wrote an event on the control channel... | ||
207 | * | ||
208 | * State of the ap->disco_XXX variables : | ||
209 | * Socket creation : discoveries = NULL ; disco_index = 0 ; disco_number = 0 | ||
210 | * While reading : discoveries = ptr ; disco_index = X ; disco_number = Y | ||
211 | * After reading : discoveries = NULL ; disco_index = Y ; disco_number = -1 | ||
212 | */ | ||
213 | static inline int | ||
214 | irnet_read_discovery_log(irnet_socket * ap, | ||
215 | char * event) | ||
216 | { | ||
217 | int done_event = 0; | ||
218 | |||
219 | DENTER(CTRL_TRACE, "(ap=0x%p, event=0x%p)\n", | ||
220 | ap, event); | ||
221 | |||
222 | /* Test if we have some work to do or we have already finished */ | ||
223 | if(ap->disco_number == -1) | ||
224 | { | ||
225 | DEBUG(CTRL_INFO, "Already done\n"); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | /* Test if it's the first time and therefore we need to get the log */ | ||
230 | if(ap->discoveries == NULL) | ||
231 | irnet_get_discovery_log(ap); | ||
232 | |||
233 | /* Check if we have more item to dump */ | ||
234 | if(ap->disco_index < ap->disco_number) | ||
235 | { | ||
236 | /* Write an event */ | ||
237 | sprintf(event, "Found %08x (%s) behind %08x {hints %02X-%02X}\n", | ||
238 | ap->discoveries[ap->disco_index].daddr, | ||
239 | ap->discoveries[ap->disco_index].info, | ||
240 | ap->discoveries[ap->disco_index].saddr, | ||
241 | ap->discoveries[ap->disco_index].hints[0], | ||
242 | ap->discoveries[ap->disco_index].hints[1]); | ||
243 | DEBUG(CTRL_INFO, "Writing discovery %d : %s\n", | ||
244 | ap->disco_index, ap->discoveries[ap->disco_index].info); | ||
245 | |||
246 | /* We have an event */ | ||
247 | done_event = 1; | ||
248 | /* Next discovery */ | ||
249 | ap->disco_index++; | ||
250 | } | ||
251 | |||
252 | /* Check if we have done the last item */ | ||
253 | if(ap->disco_index >= ap->disco_number) | ||
254 | { | ||
255 | /* No more items : remove the log and signal termination */ | ||
256 | DEBUG(CTRL_INFO, "Cleaning up log (0x%p)\n", | ||
257 | ap->discoveries); | ||
258 | if(ap->discoveries != NULL) | ||
259 | { | ||
260 | /* Cleanup our copy of the discovery log */ | ||
261 | kfree(ap->discoveries); | ||
262 | ap->discoveries = NULL; | ||
263 | } | ||
264 | ap->disco_number = -1; | ||
265 | } | ||
266 | |||
267 | return done_event; | ||
268 | } | ||
269 | #endif /* INITIAL_DISCOVERY */ | ||
270 | |||
271 | /*------------------------------------------------------------------*/ | ||
272 | /* | ||
273 | * Read is used to get IrNET events | ||
274 | */ | ||
275 | static inline ssize_t | ||
276 | irnet_ctrl_read(irnet_socket * ap, | ||
277 | struct file * file, | ||
278 | char __user * buf, | ||
279 | size_t count) | ||
280 | { | ||
281 | DECLARE_WAITQUEUE(wait, current); | ||
282 | char event[64]; /* Max event is 61 char */ | ||
283 | ssize_t ret = 0; | ||
284 | |||
285 | DENTER(CTRL_TRACE, "(ap=0x%p, count=%Zd)\n", ap, count); | ||
286 | |||
287 | /* Check if we can write an event out in one go */ | ||
288 | DABORT(count < sizeof(event), -EOVERFLOW, CTRL_ERROR, "Buffer to small.\n"); | ||
289 | |||
290 | #ifdef INITIAL_DISCOVERY | ||
291 | /* Check if we have read the log */ | ||
292 | if(irnet_read_discovery_log(ap, event)) | ||
293 | { | ||
294 | /* We have an event !!! Copy it to the user */ | ||
295 | if(copy_to_user(buf, event, strlen(event))) | ||
296 | { | ||
297 | DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); | ||
298 | return -EFAULT; | ||
299 | } | ||
300 | |||
301 | DEXIT(CTRL_TRACE, "\n"); | ||
302 | return(strlen(event)); | ||
303 | } | ||
304 | #endif /* INITIAL_DISCOVERY */ | ||
305 | |||
306 | /* Put ourselves on the wait queue to be woken up */ | ||
307 | add_wait_queue(&irnet_events.rwait, &wait); | ||
308 | current->state = TASK_INTERRUPTIBLE; | ||
309 | for(;;) | ||
310 | { | ||
311 | /* If there is unread events */ | ||
312 | ret = 0; | ||
313 | if(ap->event_index != irnet_events.index) | ||
314 | break; | ||
315 | ret = -EAGAIN; | ||
316 | if(file->f_flags & O_NONBLOCK) | ||
317 | break; | ||
318 | ret = -ERESTARTSYS; | ||
319 | if(signal_pending(current)) | ||
320 | break; | ||
321 | /* Yield and wait to be woken up */ | ||
322 | schedule(); | ||
323 | } | ||
324 | current->state = TASK_RUNNING; | ||
325 | remove_wait_queue(&irnet_events.rwait, &wait); | ||
326 | |||
327 | /* Did we got it ? */ | ||
328 | if(ret != 0) | ||
329 | { | ||
330 | /* No, return the error code */ | ||
331 | DEXIT(CTRL_TRACE, " - ret %Zd\n", ret); | ||
332 | return ret; | ||
333 | } | ||
334 | |||
335 | /* Which event is it ? */ | ||
336 | switch(irnet_events.log[ap->event_index].event) | ||
337 | { | ||
338 | case IRNET_DISCOVER: | ||
339 | sprintf(event, "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n", | ||
340 | irnet_events.log[ap->event_index].daddr, | ||
341 | irnet_events.log[ap->event_index].name, | ||
342 | irnet_events.log[ap->event_index].saddr, | ||
343 | irnet_events.log[ap->event_index].hints.byte[0], | ||
344 | irnet_events.log[ap->event_index].hints.byte[1]); | ||
345 | break; | ||
346 | case IRNET_EXPIRE: | ||
347 | sprintf(event, "Expired %08x (%s) behind %08x {hints %02X-%02X}\n", | ||
348 | irnet_events.log[ap->event_index].daddr, | ||
349 | irnet_events.log[ap->event_index].name, | ||
350 | irnet_events.log[ap->event_index].saddr, | ||
351 | irnet_events.log[ap->event_index].hints.byte[0], | ||
352 | irnet_events.log[ap->event_index].hints.byte[1]); | ||
353 | break; | ||
354 | case IRNET_CONNECT_TO: | ||
355 | sprintf(event, "Connected to %08x (%s) on ppp%d\n", | ||
356 | irnet_events.log[ap->event_index].daddr, | ||
357 | irnet_events.log[ap->event_index].name, | ||
358 | irnet_events.log[ap->event_index].unit); | ||
359 | break; | ||
360 | case IRNET_CONNECT_FROM: | ||
361 | sprintf(event, "Connection from %08x (%s) on ppp%d\n", | ||
362 | irnet_events.log[ap->event_index].daddr, | ||
363 | irnet_events.log[ap->event_index].name, | ||
364 | irnet_events.log[ap->event_index].unit); | ||
365 | break; | ||
366 | case IRNET_REQUEST_FROM: | ||
367 | sprintf(event, "Request from %08x (%s) behind %08x\n", | ||
368 | irnet_events.log[ap->event_index].daddr, | ||
369 | irnet_events.log[ap->event_index].name, | ||
370 | irnet_events.log[ap->event_index].saddr); | ||
371 | break; | ||
372 | case IRNET_NOANSWER_FROM: | ||
373 | sprintf(event, "No-answer from %08x (%s) on ppp%d\n", | ||
374 | irnet_events.log[ap->event_index].daddr, | ||
375 | irnet_events.log[ap->event_index].name, | ||
376 | irnet_events.log[ap->event_index].unit); | ||
377 | break; | ||
378 | case IRNET_BLOCKED_LINK: | ||
379 | sprintf(event, "Blocked link with %08x (%s) on ppp%d\n", | ||
380 | irnet_events.log[ap->event_index].daddr, | ||
381 | irnet_events.log[ap->event_index].name, | ||
382 | irnet_events.log[ap->event_index].unit); | ||
383 | break; | ||
384 | case IRNET_DISCONNECT_FROM: | ||
385 | sprintf(event, "Disconnection from %08x (%s) on ppp%d\n", | ||
386 | irnet_events.log[ap->event_index].daddr, | ||
387 | irnet_events.log[ap->event_index].name, | ||
388 | irnet_events.log[ap->event_index].unit); | ||
389 | break; | ||
390 | case IRNET_DISCONNECT_TO: | ||
391 | sprintf(event, "Disconnected to %08x (%s)\n", | ||
392 | irnet_events.log[ap->event_index].daddr, | ||
393 | irnet_events.log[ap->event_index].name); | ||
394 | break; | ||
395 | default: | ||
396 | sprintf(event, "Bug\n"); | ||
397 | } | ||
398 | /* Increment our event index */ | ||
399 | ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS; | ||
400 | |||
401 | DEBUG(CTRL_INFO, "Event is :%s", event); | ||
402 | |||
403 | /* Copy it to the user */ | ||
404 | if(copy_to_user(buf, event, strlen(event))) | ||
405 | { | ||
406 | DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); | ||
407 | return -EFAULT; | ||
408 | } | ||
409 | |||
410 | DEXIT(CTRL_TRACE, "\n"); | ||
411 | return(strlen(event)); | ||
412 | } | ||
413 | |||
414 | /*------------------------------------------------------------------*/ | ||
415 | /* | ||
416 | * Poll : called when someone do a select on /dev/irnet. | ||
417 | * Just check if there are new events... | ||
418 | */ | ||
419 | static inline unsigned int | ||
420 | irnet_ctrl_poll(irnet_socket * ap, | ||
421 | struct file * file, | ||
422 | poll_table * wait) | ||
423 | { | ||
424 | unsigned int mask; | ||
425 | |||
426 | DENTER(CTRL_TRACE, "(ap=0x%p)\n", ap); | ||
427 | |||
428 | poll_wait(file, &irnet_events.rwait, wait); | ||
429 | mask = POLLOUT | POLLWRNORM; | ||
430 | /* If there is unread events */ | ||
431 | if(ap->event_index != irnet_events.index) | ||
432 | mask |= POLLIN | POLLRDNORM; | ||
433 | #ifdef INITIAL_DISCOVERY | ||
434 | if(ap->disco_number != -1) | ||
435 | { | ||
436 | /* Test if it's the first time and therefore we need to get the log */ | ||
437 | if(ap->discoveries == NULL) | ||
438 | irnet_get_discovery_log(ap); | ||
439 | /* Recheck */ | ||
440 | if(ap->disco_number != -1) | ||
441 | mask |= POLLIN | POLLRDNORM; | ||
442 | } | ||
443 | #endif /* INITIAL_DISCOVERY */ | ||
444 | |||
445 | DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask); | ||
446 | return mask; | ||
447 | } | ||
448 | |||
449 | |||
450 | /*********************** FILESYSTEM CALLBACKS ***********************/ | ||
451 | /* | ||
452 | * Implement the usual open, read, write functions that will be called | ||
453 | * by the file system when some action is performed on /dev/irnet. | ||
454 | * Most of those actions will in fact be performed by "pppd" or | ||
455 | * the control channel, we just act as a redirector... | ||
456 | */ | ||
457 | |||
458 | /*------------------------------------------------------------------*/ | ||
459 | /* | ||
460 | * Open : when somebody open /dev/irnet | ||
461 | * We basically create a new instance of irnet and initialise it. | ||
462 | */ | ||
463 | static int | ||
464 | dev_irnet_open(struct inode * inode, | ||
465 | struct file * file) | ||
466 | { | ||
467 | struct irnet_socket * ap; | ||
468 | int err; | ||
469 | |||
470 | DENTER(FS_TRACE, "(file=0x%p)\n", file); | ||
471 | |||
472 | #ifdef SECURE_DEVIRNET | ||
473 | /* This could (should?) be enforced by the permissions on /dev/irnet. */ | ||
474 | if(!capable(CAP_NET_ADMIN)) | ||
475 | return -EPERM; | ||
476 | #endif /* SECURE_DEVIRNET */ | ||
477 | |||
478 | /* Allocate a private structure for this IrNET instance */ | ||
479 | ap = kmalloc(sizeof(*ap), GFP_KERNEL); | ||
480 | DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n"); | ||
481 | |||
482 | /* initialize the irnet structure */ | ||
483 | memset(ap, 0, sizeof(*ap)); | ||
484 | ap->file = file; | ||
485 | |||
486 | /* PPP channel setup */ | ||
487 | ap->ppp_open = 0; | ||
488 | ap->chan.private = ap; | ||
489 | ap->chan.ops = &irnet_ppp_ops; | ||
490 | ap->chan.mtu = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); | ||
491 | ap->chan.hdrlen = 2 + TTP_MAX_HEADER; /* for A/C + Max IrDA hdr */ | ||
492 | /* PPP parameters */ | ||
493 | ap->mru = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); | ||
494 | ap->xaccm[0] = ~0U; | ||
495 | ap->xaccm[3] = 0x60000000U; | ||
496 | ap->raccm = ~0U; | ||
497 | |||
498 | /* Setup the IrDA part... */ | ||
499 | err = irda_irnet_create(ap); | ||
500 | if(err) | ||
501 | { | ||
502 | DERROR(FS_ERROR, "Can't setup IrDA link...\n"); | ||
503 | kfree(ap); | ||
504 | return err; | ||
505 | } | ||
506 | |||
507 | /* For the control channel */ | ||
508 | ap->event_index = irnet_events.index; /* Cancel all past events */ | ||
509 | |||
510 | /* Put our stuff where we will be able to find it later */ | ||
511 | file->private_data = ap; | ||
512 | |||
513 | DEXIT(FS_TRACE, " - ap=0x%p\n", ap); | ||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | |||
518 | /*------------------------------------------------------------------*/ | ||
519 | /* | ||
520 | * Close : when somebody close /dev/irnet | ||
521 | * Destroy the instance of /dev/irnet | ||
522 | */ | ||
523 | static int | ||
524 | dev_irnet_close(struct inode * inode, | ||
525 | struct file * file) | ||
526 | { | ||
527 | irnet_socket * ap = (struct irnet_socket *) file->private_data; | ||
528 | |||
529 | DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", | ||
530 | file, ap); | ||
531 | DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n"); | ||
532 | |||
533 | /* Detach ourselves */ | ||
534 | file->private_data = NULL; | ||
535 | |||
536 | /* Close IrDA stuff */ | ||
537 | irda_irnet_destroy(ap); | ||
538 | |||
539 | /* Disconnect from the generic PPP layer if not already done */ | ||
540 | if(ap->ppp_open) | ||
541 | { | ||
542 | DERROR(FS_ERROR, "Channel still registered - deregistering !\n"); | ||
543 | ap->ppp_open = 0; | ||
544 | ppp_unregister_channel(&ap->chan); | ||
545 | } | ||
546 | |||
547 | kfree(ap); | ||
548 | |||
549 | DEXIT(FS_TRACE, "\n"); | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | /*------------------------------------------------------------------*/ | ||
554 | /* | ||
555 | * Write does nothing. | ||
556 | * (we receive packet from ppp_generic through ppp_irnet_send()) | ||
557 | */ | ||
558 | static ssize_t | ||
559 | dev_irnet_write(struct file * file, | ||
560 | const char __user *buf, | ||
561 | size_t count, | ||
562 | loff_t * ppos) | ||
563 | { | ||
564 | irnet_socket * ap = (struct irnet_socket *) file->private_data; | ||
565 | |||
566 | DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%Zd)\n", | ||
567 | file, ap, count); | ||
568 | DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); | ||
569 | |||
570 | /* If we are connected to ppp_generic, let it handle the job */ | ||
571 | if(ap->ppp_open) | ||
572 | return -EAGAIN; | ||
573 | else | ||
574 | return irnet_ctrl_write(ap, buf, count); | ||
575 | } | ||
576 | |||
577 | /*------------------------------------------------------------------*/ | ||
578 | /* | ||
579 | * Read doesn't do much either. | ||
580 | * (pppd poll us, but ultimately reads through /dev/ppp) | ||
581 | */ | ||
582 | static ssize_t | ||
583 | dev_irnet_read(struct file * file, | ||
584 | char __user * buf, | ||
585 | size_t count, | ||
586 | loff_t * ppos) | ||
587 | { | ||
588 | irnet_socket * ap = (struct irnet_socket *) file->private_data; | ||
589 | |||
590 | DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%Zd)\n", | ||
591 | file, ap, count); | ||
592 | DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); | ||
593 | |||
594 | /* If we are connected to ppp_generic, let it handle the job */ | ||
595 | if(ap->ppp_open) | ||
596 | return -EAGAIN; | ||
597 | else | ||
598 | return irnet_ctrl_read(ap, file, buf, count); | ||
599 | } | ||
600 | |||
601 | /*------------------------------------------------------------------*/ | ||
602 | /* | ||
603 | * Poll : called when someone do a select on /dev/irnet | ||
604 | */ | ||
605 | static unsigned int | ||
606 | dev_irnet_poll(struct file * file, | ||
607 | poll_table * wait) | ||
608 | { | ||
609 | irnet_socket * ap = (struct irnet_socket *) file->private_data; | ||
610 | unsigned int mask; | ||
611 | |||
612 | DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", | ||
613 | file, ap); | ||
614 | |||
615 | mask = POLLOUT | POLLWRNORM; | ||
616 | DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n"); | ||
617 | |||
618 | /* If we are connected to ppp_generic, let it handle the job */ | ||
619 | if(!ap->ppp_open) | ||
620 | mask |= irnet_ctrl_poll(ap, file, wait); | ||
621 | |||
622 | DEXIT(FS_TRACE, " - mask=0x%X\n", mask); | ||
623 | return(mask); | ||
624 | } | ||
625 | |||
626 | /*------------------------------------------------------------------*/ | ||
627 | /* | ||
628 | * IOCtl : Called when someone does some ioctls on /dev/irnet | ||
629 | * This is the way pppd configure us and control us while the PPP | ||
630 | * instance is active. | ||
631 | */ | ||
632 | static int | ||
633 | dev_irnet_ioctl(struct inode * inode, | ||
634 | struct file * file, | ||
635 | unsigned int cmd, | ||
636 | unsigned long arg) | ||
637 | { | ||
638 | irnet_socket * ap = (struct irnet_socket *) file->private_data; | ||
639 | int err; | ||
640 | int val; | ||
641 | void __user *argp = (void __user *)arg; | ||
642 | |||
643 | DENTER(FS_TRACE, "(file=0x%p, ap=0x%p, cmd=0x%X)\n", | ||
644 | file, ap, cmd); | ||
645 | |||
646 | /* Basic checks... */ | ||
647 | DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); | ||
648 | #ifdef SECURE_DEVIRNET | ||
649 | if(!capable(CAP_NET_ADMIN)) | ||
650 | return -EPERM; | ||
651 | #endif /* SECURE_DEVIRNET */ | ||
652 | |||
653 | err = -EFAULT; | ||
654 | switch(cmd) | ||
655 | { | ||
656 | /* Set discipline (should be N_SYNC_PPP or N_TTY) */ | ||
657 | case TIOCSETD: | ||
658 | if(get_user(val, (int __user *)argp)) | ||
659 | break; | ||
660 | if((val == N_SYNC_PPP) || (val == N_PPP)) | ||
661 | { | ||
662 | DEBUG(FS_INFO, "Entering PPP discipline.\n"); | ||
663 | /* PPP channel setup (ap->chan in configued in dev_irnet_open())*/ | ||
664 | err = ppp_register_channel(&ap->chan); | ||
665 | if(err == 0) | ||
666 | { | ||
667 | /* Our ppp side is active */ | ||
668 | ap->ppp_open = 1; | ||
669 | |||
670 | DEBUG(FS_INFO, "Trying to establish a connection.\n"); | ||
671 | /* Setup the IrDA link now - may fail... */ | ||
672 | irda_irnet_connect(ap); | ||
673 | } | ||
674 | else | ||
675 | DERROR(FS_ERROR, "Can't setup PPP channel...\n"); | ||
676 | } | ||
677 | else | ||
678 | { | ||
679 | /* In theory, should be N_TTY */ | ||
680 | DEBUG(FS_INFO, "Exiting PPP discipline.\n"); | ||
681 | /* Disconnect from the generic PPP layer */ | ||
682 | if(ap->ppp_open) | ||
683 | { | ||
684 | ap->ppp_open = 0; | ||
685 | ppp_unregister_channel(&ap->chan); | ||
686 | } | ||
687 | else | ||
688 | DERROR(FS_ERROR, "Channel not registered !\n"); | ||
689 | err = 0; | ||
690 | } | ||
691 | break; | ||
692 | |||
693 | /* Query PPP channel and unit number */ | ||
694 | case PPPIOCGCHAN: | ||
695 | if(!ap->ppp_open) | ||
696 | break; | ||
697 | if(put_user(ppp_channel_index(&ap->chan), (int __user *)argp)) | ||
698 | break; | ||
699 | DEBUG(FS_INFO, "Query channel.\n"); | ||
700 | err = 0; | ||
701 | break; | ||
702 | case PPPIOCGUNIT: | ||
703 | if(!ap->ppp_open) | ||
704 | break; | ||
705 | if(put_user(ppp_unit_number(&ap->chan), (int __user *)argp)) | ||
706 | break; | ||
707 | DEBUG(FS_INFO, "Query unit number.\n"); | ||
708 | err = 0; | ||
709 | break; | ||
710 | |||
711 | /* All these ioctls can be passed both directly and from ppp_generic, | ||
712 | * so we just deal with them in one place... | ||
713 | */ | ||
714 | case PPPIOCGFLAGS: | ||
715 | case PPPIOCSFLAGS: | ||
716 | case PPPIOCGASYNCMAP: | ||
717 | case PPPIOCSASYNCMAP: | ||
718 | case PPPIOCGRASYNCMAP: | ||
719 | case PPPIOCSRASYNCMAP: | ||
720 | case PPPIOCGXASYNCMAP: | ||
721 | case PPPIOCSXASYNCMAP: | ||
722 | case PPPIOCGMRU: | ||
723 | case PPPIOCSMRU: | ||
724 | DEBUG(FS_INFO, "Standard PPP ioctl.\n"); | ||
725 | if(!capable(CAP_NET_ADMIN)) | ||
726 | err = -EPERM; | ||
727 | else | ||
728 | err = ppp_irnet_ioctl(&ap->chan, cmd, arg); | ||
729 | break; | ||
730 | |||
731 | /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */ | ||
732 | /* Get termios */ | ||
733 | case TCGETS: | ||
734 | DEBUG(FS_INFO, "Get termios.\n"); | ||
735 | if(kernel_termios_to_user_termios((struct termios __user *)argp, &ap->termios)) | ||
736 | break; | ||
737 | err = 0; | ||
738 | break; | ||
739 | /* Set termios */ | ||
740 | case TCSETSF: | ||
741 | DEBUG(FS_INFO, "Set termios.\n"); | ||
742 | if(user_termios_to_kernel_termios(&ap->termios, (struct termios __user *)argp)) | ||
743 | break; | ||
744 | err = 0; | ||
745 | break; | ||
746 | |||
747 | /* Set DTR/RTS */ | ||
748 | case TIOCMBIS: | ||
749 | case TIOCMBIC: | ||
750 | /* Set exclusive/non-exclusive mode */ | ||
751 | case TIOCEXCL: | ||
752 | case TIOCNXCL: | ||
753 | DEBUG(FS_INFO, "TTY compatibility.\n"); | ||
754 | err = 0; | ||
755 | break; | ||
756 | |||
757 | case TCGETA: | ||
758 | DEBUG(FS_INFO, "TCGETA\n"); | ||
759 | break; | ||
760 | |||
761 | case TCFLSH: | ||
762 | DEBUG(FS_INFO, "TCFLSH\n"); | ||
763 | /* Note : this will flush buffers in PPP, so it *must* be done | ||
764 | * We should also worry that we don't accept junk here and that | ||
765 | * we get rid of our own buffers */ | ||
766 | #ifdef FLUSH_TO_PPP | ||
767 | ppp_output_wakeup(&ap->chan); | ||
768 | #endif /* FLUSH_TO_PPP */ | ||
769 | err = 0; | ||
770 | break; | ||
771 | |||
772 | case FIONREAD: | ||
773 | DEBUG(FS_INFO, "FIONREAD\n"); | ||
774 | val = 0; | ||
775 | if(put_user(val, (int __user *)argp)) | ||
776 | break; | ||
777 | err = 0; | ||
778 | break; | ||
779 | |||
780 | default: | ||
781 | DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd); | ||
782 | err = -ENOIOCTLCMD; | ||
783 | } | ||
784 | |||
785 | DEXIT(FS_TRACE, " - err = 0x%X\n", err); | ||
786 | return err; | ||
787 | } | ||
788 | |||
789 | /************************** PPP CALLBACKS **************************/ | ||
790 | /* | ||
791 | * This are the functions that the generic PPP driver in the kernel | ||
792 | * will call to communicate to us. | ||
793 | */ | ||
794 | |||
795 | /*------------------------------------------------------------------*/ | ||
796 | /* | ||
797 | * Prepare the ppp frame for transmission over the IrDA socket. | ||
798 | * We make sure that the header space is enough, and we change ppp header | ||
799 | * according to flags passed by pppd. | ||
800 | * This is not a callback, but just a helper function used in ppp_irnet_send() | ||
801 | */ | ||
802 | static inline struct sk_buff * | ||
803 | irnet_prepare_skb(irnet_socket * ap, | ||
804 | struct sk_buff * skb) | ||
805 | { | ||
806 | unsigned char * data; | ||
807 | int proto; /* PPP protocol */ | ||
808 | int islcp; /* Protocol == LCP */ | ||
809 | int needaddr; /* Need PPP address */ | ||
810 | |||
811 | DENTER(PPP_TRACE, "(ap=0x%p, skb=0x%p)\n", | ||
812 | ap, skb); | ||
813 | |||
814 | /* Extract PPP protocol from the frame */ | ||
815 | data = skb->data; | ||
816 | proto = (data[0] << 8) + data[1]; | ||
817 | |||
818 | /* LCP packets with codes between 1 (configure-request) | ||
819 | * and 7 (code-reject) must be sent as though no options | ||
820 | * have been negotiated. */ | ||
821 | islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7); | ||
822 | |||
823 | /* compress protocol field if option enabled */ | ||
824 | if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp)) | ||
825 | skb_pull(skb,1); | ||
826 | |||
827 | /* Check if we need address/control fields */ | ||
828 | needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp); | ||
829 | |||
830 | /* Is the skb headroom large enough to contain all IrDA-headers? */ | ||
831 | if((skb_headroom(skb) < (ap->max_header_size + needaddr)) || | ||
832 | (skb_shared(skb))) | ||
833 | { | ||
834 | struct sk_buff * new_skb; | ||
835 | |||
836 | DEBUG(PPP_INFO, "Reallocating skb\n"); | ||
837 | |||
838 | /* Create a new skb */ | ||
839 | new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr); | ||
840 | |||
841 | /* We have to free the original skb anyway */ | ||
842 | dev_kfree_skb(skb); | ||
843 | |||
844 | /* Did the realloc succeed ? */ | ||
845 | DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n"); | ||
846 | |||
847 | /* Use the new skb instead */ | ||
848 | skb = new_skb; | ||
849 | } | ||
850 | |||
851 | /* prepend address/control fields if necessary */ | ||
852 | if(needaddr) | ||
853 | { | ||
854 | skb_push(skb, 2); | ||
855 | skb->data[0] = PPP_ALLSTATIONS; | ||
856 | skb->data[1] = PPP_UI; | ||
857 | } | ||
858 | |||
859 | DEXIT(PPP_TRACE, "\n"); | ||
860 | |||
861 | return skb; | ||
862 | } | ||
863 | |||
864 | /*------------------------------------------------------------------*/ | ||
865 | /* | ||
866 | * Send a packet to the peer over the IrTTP connection. | ||
867 | * Returns 1 iff the packet was accepted. | ||
868 | * Returns 0 iff packet was not consumed. | ||
869 | * If the packet was not accepted, we will call ppp_output_wakeup | ||
870 | * at some later time to reactivate flow control in ppp_generic. | ||
871 | */ | ||
872 | static int | ||
873 | ppp_irnet_send(struct ppp_channel * chan, | ||
874 | struct sk_buff * skb) | ||
875 | { | ||
876 | irnet_socket * self = (struct irnet_socket *) chan->private; | ||
877 | int ret; | ||
878 | |||
879 | DENTER(PPP_TRACE, "(channel=0x%p, ap/self=0x%p)\n", | ||
880 | chan, self); | ||
881 | |||
882 | /* Check if things are somewhat valid... */ | ||
883 | DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n"); | ||
884 | |||
885 | /* Check if we are connected */ | ||
886 | if(!(test_bit(0, &self->ttp_open))) | ||
887 | { | ||
888 | #ifdef CONNECT_IN_SEND | ||
889 | /* Let's try to connect one more time... */ | ||
890 | /* Note : we won't be connected after this call, but we should be | ||
891 | * ready for next packet... */ | ||
892 | /* If we are already connecting, this will fail */ | ||
893 | irda_irnet_connect(self); | ||
894 | #endif /* CONNECT_IN_SEND */ | ||
895 | |||
896 | DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n", | ||
897 | self->ttp_open, self->ttp_connect); | ||
898 | |||
899 | /* Note : we can either drop the packet or block the packet. | ||
900 | * | ||
901 | * Blocking the packet allow us a better connection time, | ||
902 | * because by calling ppp_output_wakeup() we can have | ||
903 | * ppp_generic resending the LCP request immediately to us, | ||
904 | * rather than waiting for one of pppd periodic transmission of | ||
905 | * LCP request. | ||
906 | * | ||
907 | * On the other hand, if we block all packet, all those periodic | ||
908 | * transmissions of pppd accumulate in ppp_generic, creating a | ||
909 | * backlog of LCP request. When we eventually connect later on, | ||
910 | * we have to transmit all this backlog before we can connect | ||
911 | * proper (if we don't timeout before). | ||
912 | * | ||
913 | * The current strategy is as follow : | ||
914 | * While we are attempting to connect, we block packets to get | ||
915 | * a better connection time. | ||
916 | * If we fail to connect, we drain the queue and start dropping packets | ||
917 | */ | ||
918 | #ifdef BLOCK_WHEN_CONNECT | ||
919 | /* If we are attempting to connect */ | ||
920 | if(test_bit(0, &self->ttp_connect)) | ||
921 | { | ||
922 | /* Blocking packet, ppp_generic will retry later */ | ||
923 | return 0; | ||
924 | } | ||
925 | #endif /* BLOCK_WHEN_CONNECT */ | ||
926 | |||
927 | /* Dropping packet, pppd will retry later */ | ||
928 | dev_kfree_skb(skb); | ||
929 | return 1; | ||
930 | } | ||
931 | |||
932 | /* Check if the queue can accept any packet, otherwise block */ | ||
933 | if(self->tx_flow != FLOW_START) | ||
934 | DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n", | ||
935 | skb_queue_len(&self->tsap->tx_queue)); | ||
936 | |||
937 | /* Prepare ppp frame for transmission */ | ||
938 | skb = irnet_prepare_skb(self, skb); | ||
939 | DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n"); | ||
940 | |||
941 | /* Send the packet to IrTTP */ | ||
942 | ret = irttp_data_request(self->tsap, skb); | ||
943 | if(ret < 0) | ||
944 | { | ||
945 | /* | ||
946 | * > IrTTPs tx queue is full, so we just have to | ||
947 | * > drop the frame! You might think that we should | ||
948 | * > just return -1 and don't deallocate the frame, | ||
949 | * > but that is dangerous since it's possible that | ||
950 | * > we have replaced the original skb with a new | ||
951 | * > one with larger headroom, and that would really | ||
952 | * > confuse do_dev_queue_xmit() in dev.c! I have | ||
953 | * > tried :-) DB | ||
954 | * Correction : we verify the flow control above (self->tx_flow), | ||
955 | * so we come here only if IrTTP doesn't like the packet (empty, | ||
956 | * too large, IrTTP not connected). In those rare cases, it's ok | ||
957 | * to drop it, we don't want to see it here again... | ||
958 | * Jean II | ||
959 | */ | ||
960 | DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret); | ||
961 | /* irttp_data_request already free the packet */ | ||
962 | } | ||
963 | |||
964 | DEXIT(PPP_TRACE, "\n"); | ||
965 | return 1; /* Packet has been consumed */ | ||
966 | } | ||
967 | |||
968 | /*------------------------------------------------------------------*/ | ||
969 | /* | ||
970 | * Take care of the ioctls that ppp_generic doesn't want to deal with... | ||
971 | * Note : we are also called from dev_irnet_ioctl(). | ||
972 | */ | ||
973 | static int | ||
974 | ppp_irnet_ioctl(struct ppp_channel * chan, | ||
975 | unsigned int cmd, | ||
976 | unsigned long arg) | ||
977 | { | ||
978 | irnet_socket * ap = (struct irnet_socket *) chan->private; | ||
979 | int err; | ||
980 | int val; | ||
981 | u32 accm[8]; | ||
982 | void __user *argp = (void __user *)arg; | ||
983 | |||
984 | DENTER(PPP_TRACE, "(channel=0x%p, ap=0x%p, cmd=0x%X)\n", | ||
985 | chan, ap, cmd); | ||
986 | |||
987 | /* Basic checks... */ | ||
988 | DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); | ||
989 | |||
990 | err = -EFAULT; | ||
991 | switch(cmd) | ||
992 | { | ||
993 | /* PPP flags */ | ||
994 | case PPPIOCGFLAGS: | ||
995 | val = ap->flags | ap->rbits; | ||
996 | if(put_user(val, (int __user *) argp)) | ||
997 | break; | ||
998 | err = 0; | ||
999 | break; | ||
1000 | case PPPIOCSFLAGS: | ||
1001 | if(get_user(val, (int __user *) argp)) | ||
1002 | break; | ||
1003 | ap->flags = val & ~SC_RCV_BITS; | ||
1004 | ap->rbits = val & SC_RCV_BITS; | ||
1005 | err = 0; | ||
1006 | break; | ||
1007 | |||
1008 | /* Async map stuff - all dummy to please pppd */ | ||
1009 | case PPPIOCGASYNCMAP: | ||
1010 | if(put_user(ap->xaccm[0], (u32 __user *) argp)) | ||
1011 | break; | ||
1012 | err = 0; | ||
1013 | break; | ||
1014 | case PPPIOCSASYNCMAP: | ||
1015 | if(get_user(ap->xaccm[0], (u32 __user *) argp)) | ||
1016 | break; | ||
1017 | err = 0; | ||
1018 | break; | ||
1019 | case PPPIOCGRASYNCMAP: | ||
1020 | if(put_user(ap->raccm, (u32 __user *) argp)) | ||
1021 | break; | ||
1022 | err = 0; | ||
1023 | break; | ||
1024 | case PPPIOCSRASYNCMAP: | ||
1025 | if(get_user(ap->raccm, (u32 __user *) argp)) | ||
1026 | break; | ||
1027 | err = 0; | ||
1028 | break; | ||
1029 | case PPPIOCGXASYNCMAP: | ||
1030 | if(copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm))) | ||
1031 | break; | ||
1032 | err = 0; | ||
1033 | break; | ||
1034 | case PPPIOCSXASYNCMAP: | ||
1035 | if(copy_from_user(accm, argp, sizeof(accm))) | ||
1036 | break; | ||
1037 | accm[2] &= ~0x40000000U; /* can't escape 0x5e */ | ||
1038 | accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ | ||
1039 | memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); | ||
1040 | err = 0; | ||
1041 | break; | ||
1042 | |||
1043 | /* Max PPP frame size */ | ||
1044 | case PPPIOCGMRU: | ||
1045 | if(put_user(ap->mru, (int __user *) argp)) | ||
1046 | break; | ||
1047 | err = 0; | ||
1048 | break; | ||
1049 | case PPPIOCSMRU: | ||
1050 | if(get_user(val, (int __user *) argp)) | ||
1051 | break; | ||
1052 | if(val < PPP_MRU) | ||
1053 | val = PPP_MRU; | ||
1054 | ap->mru = val; | ||
1055 | err = 0; | ||
1056 | break; | ||
1057 | |||
1058 | default: | ||
1059 | DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd); | ||
1060 | err = -ENOIOCTLCMD; | ||
1061 | } | ||
1062 | |||
1063 | DEXIT(PPP_TRACE, " - err = 0x%X\n", err); | ||
1064 | return err; | ||
1065 | } | ||
1066 | |||
1067 | /************************** INITIALISATION **************************/ | ||
1068 | /* | ||
1069 | * Module initialisation and all that jazz... | ||
1070 | */ | ||
1071 | |||
1072 | /*------------------------------------------------------------------*/ | ||
1073 | /* | ||
1074 | * Hook our device callbacks in the filesystem, to connect our code | ||
1075 | * to /dev/irnet | ||
1076 | */ | ||
1077 | static inline int __init | ||
1078 | ppp_irnet_init(void) | ||
1079 | { | ||
1080 | int err = 0; | ||
1081 | |||
1082 | DENTER(MODULE_TRACE, "()\n"); | ||
1083 | |||
1084 | /* Allocate ourselves as a minor in the misc range */ | ||
1085 | err = misc_register(&irnet_misc_device); | ||
1086 | |||
1087 | DEXIT(MODULE_TRACE, "\n"); | ||
1088 | return err; | ||
1089 | } | ||
1090 | |||
1091 | /*------------------------------------------------------------------*/ | ||
1092 | /* | ||
1093 | * Cleanup at exit... | ||
1094 | */ | ||
1095 | static inline void __exit | ||
1096 | ppp_irnet_cleanup(void) | ||
1097 | { | ||
1098 | DENTER(MODULE_TRACE, "()\n"); | ||
1099 | |||
1100 | /* De-allocate /dev/irnet minor in misc range */ | ||
1101 | misc_deregister(&irnet_misc_device); | ||
1102 | |||
1103 | DEXIT(MODULE_TRACE, "\n"); | ||
1104 | } | ||
1105 | |||
1106 | /*------------------------------------------------------------------*/ | ||
1107 | /* | ||
1108 | * Module main entry point | ||
1109 | */ | ||
1110 | int __init | ||
1111 | irnet_init(void) | ||
1112 | { | ||
1113 | int err; | ||
1114 | |||
1115 | /* Initialise both parts... */ | ||
1116 | err = irda_irnet_init(); | ||
1117 | if(!err) | ||
1118 | err = ppp_irnet_init(); | ||
1119 | return err; | ||
1120 | } | ||
1121 | |||
1122 | /*------------------------------------------------------------------*/ | ||
1123 | /* | ||
1124 | * Module exit | ||
1125 | */ | ||
1126 | static void __exit | ||
1127 | irnet_cleanup(void) | ||
1128 | { | ||
1129 | irda_irnet_cleanup(); | ||
1130 | ppp_irnet_cleanup(); | ||
1131 | } | ||
1132 | |||
1133 | /*------------------------------------------------------------------*/ | ||
1134 | /* | ||
1135 | * Module magic | ||
1136 | */ | ||
1137 | module_init(irnet_init); | ||
1138 | module_exit(irnet_cleanup); | ||
1139 | MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>"); | ||
1140 | MODULE_DESCRIPTION("IrNET : Synchronous PPP over IrDA"); | ||
1141 | MODULE_LICENSE("GPL"); | ||
1142 | MODULE_ALIAS_CHARDEV(10, 187); | ||