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