/*
* IrNET protocol module : Synchronous PPP over an IrDA socket.
*
* Jean II - HPL `00 - <jt@hpl.hp.com>
*
* This file implement the PPP interface and /dev/irnet character device.
* The PPP interface hook to the ppp_generic module, handle all our
* relationship to the PPP code in the kernel (and by extension to pppd),
* and exchange PPP frames with this module (send/receive).
* The /dev/irnet device is used primarily for 2 functions :
* 1) as a stub for pppd (the ppp daemon), so that we can appropriately
* generate PPP sessions (we pretend we are a tty).
* 2) as a control channel (write commands, read events)
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include "irnet_ppp.h" /* Private header */
/* Please put other headers in irnet.h - Thanks */
/* Generic PPP callbacks (to call us) */
static const struct ppp_channel_ops irnet_ppp_ops = {
.start_xmit = ppp_irnet_send,
.ioctl = ppp_irnet_ioctl
};
/************************* CONTROL CHANNEL *************************/
/*
* When a pppd instance is not active on /dev/irnet, it acts as a control
* channel.
* Writing allow to set up the IrDA destination of the IrNET channel,
* and any application may be read events happening in IrNET...
*/
/*------------------------------------------------------------------*/
/*
* Write is used to send a command to configure a IrNET channel
* before it is open by pppd. The syntax is : "command argument"
* Currently there is only two defined commands :
* o name : set the requested IrDA nickname of the IrNET peer.
* o addr : set the requested IrDA address of the IrNET peer.
* Note : the code is crude, but effective...
*/
static inline ssize_t
irnet_ctrl_write(irnet_socket * ap,
const char __user *buf,
size_t count)
{
char command[IRNET_MAX_COMMAND];
char * start; /* Current command being processed */
char * next; /* Next command to process */
int length; /* Length of current command */
DENTER(CTRL_TRACE, "(ap=0x%p, count=%Zd)\n", ap, count);
/* Check for overflow... */
DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM,
CTRL_ERROR, "Too much data !!!\n");
/* Get the data in the driver */
if(copy_from_user(command, buf, count))
{
DERROR(CTRL_ERROR, "Invalid user space pointer.\n");
return -EFAULT;
}
/* Safe terminate the string */
command[count] = '\0';
DEBUG(CTRL_INFO, "Command line received is ``%s'' (%Zd).\n",
command, count);
/* Check every commands in the command line */
next = command;
while(next != NULL)
{
/* Look at the next command */
start = next;
/* Scrap whitespaces before the command */
start = skip_spaces(start);
/* ',' is our command separator */
next = strchr(start, ',');
if(next)
{
*next = '\0'; /* Terminate command */
length = next - start; /* Length */
next++; /* Skip the '\0' */
}
else
length = strlen(start);
DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length);
/* Check if we recognised one of the known command
* We can't use "switch" with strings, so hack with "continue" */
/* First command : name -> Requested IrDA nickname */
if(!strncmp(start, "name", 4))
{
/* Copy the name only if is included and not "any" */
if((length > 5) && (strcmp(start + 5, "any")))
{
/* Strip out trailing whitespaces */
while(isspace(start[length - 1]))
length--;
DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5,
-EINVAL, CTRL_ERROR, "Invalid nickname.\n");
/* Copy the name for later reuse */
memcpy(ap->rname, start + 5, length - 5);
ap->rname[length - 5] = '\0';
}
else
ap->rname[0] = '\0';
DEBUG(CTRL_INFO, "Got rname = ``%s''