Listing 1: read, write, and llseek entry points

ssize_t
RobotArmController_read(struct file * apFile,
char * apBuffer, size_t aLength, loff_t * aOffset)
{
    RobotArmControllerData * pDevice = 0;
    ssize_t bytesRead = 0;

    if( !apFile || !apFile->private_data )
    {
        printk(KERN_INFO "ERROR in RobotArmController_read: \
                 NULL input detected.\n");
        return -EINVAL;
    }
    pDevice = apFile->private_data;
    if( pDevice->mOffset > *aOffset  )
    {    *aOffset = pDevice->mOffset;
    }

    if( *aOffset >= pDevice->mSize )
    {
        *aOffset = 0;
        pDevice->mOffset = *aOffset;
        return 0;
    }

    if (down_interruptible(&gSem))
    {
        printk(KERN_INFO "Error in RobotArmController_read, \
               down_interruptible call.\n");
        return -ERESTARTSYS;
    }

    if (copy_to_user(apBuffer, &pDevice->mpBuffer[*aOffset],
        pDevice->mSize - *aOffset))
    {
        printk(KERN_INFO "Error in RobotArmController_read, \
                copy_from_user routine.\n");
        up(&gSem);
        return -EFAULT;
    }

    bytesRead = pDevice->mSize - *aOffset;
    *aOffset += bytesRead;
    pDevice->mOffset = *aOffset;

    up(&gSem);
    return bytesRead;
}

ssize_t
RobotArmController_write( struct file *     apFile,
                      const char *         apBuffer,
                      size_t               aLength,
                      loff_t *             aOffset)
{
    RobotArmControllerData * pDevice = 0;
    if( !apFile || !apFile->private_data )
    {
        printk(KERN_INFO "ERROR in RobotArmController_write: \
                NULL input detected.\n");
        return -EINVAL;
    }
    pDevice = apFile->private_data;

    if( aLength >  MAX_FULL_DRIVER_STORAGE - 1)
    {
        printk(KERN_INFO "ERROR in RobotArmController_write: \
                Input is too long to accept.\n");
        return -ENOMEM;
    }
    if (down_interruptible(&gSem))
    {
        printk(KERN_INFO "Error in RobotArmController_write, \
                down_interruptible call.\n");
        return -ERESTARTSYS;
    }

    if (copy_from_user(pDevice->mpBuffer, apBuffer, aLength))
    {
        printk(KERN_INFO "Error in RobotArmController_write, \
                copy_from_user routine.\n");
        up(&gSem);
        return -EFAULT;
    }
    pDevice->mpBuffer[aLength] = '\0';
    pDevice->mSize = aLength;
    *aOffset += aLength;
    up(&gSem);
    return aLength;
}

loff_t RobotArmController_llseek(struct file * apFile,
                                 loff_t aOffset, int whence)
{
    // return -ESPIPE; //This is how to disable llseek.
    RobotArmControllerData * pDevice = 0;
    loff_t           newPosition = 0;

    if( !apFile || !apFile->private_data )
    {
        printk(KERN_INFO "ERROR in RobotArmController_llseek: \
                NULL input detected.\n");
        return -EINVAL;
    }
    pDevice = apFile->private_data;

    switch(whence)
    {
    case 0: // SEEK_SET
        newPosition = aOffset;
        break;
    case 1: // SEEK_CUR
        newPosition = pDevice->mOffset + aOffset;
        break;
    case 2: // SEEK_END
        newPosition = pDevice->mSize + aOffset;
        break;
    default:
        return -EINVAL;
    }
    if( newPosition < 0 )
    {    return -EINVAL;
    }
    pDevice->mOffset = newPosition;
    return newPosition;
}