android_kernel_modules_leno.../drivers/nfc/intel/fdp_custom.c

896 lines
22 KiB
C

/* -------------------------------------------------------------------------
* Copyright (C) 2010 Inside Secure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------
* Copyright (C) 2014-2016, Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ------------------------------------------------------------------------- */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <asm/delay.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
#define __devexit_p(x) x
#endif
#include "fdp_main.h"
/* define to debug all function calls */
//#define TRACE_THIS_MODULE
/***************************************/
/****** Platform dependent ***********/
/* I2C parameters, must be in sync with your board configuration */
#define I2C_ID_NAME "fdp"
#define I2C_DEVICE_ADDR 0x5E
/***************************************/
#define I2C_WRITE_DATA_LENGTH 288 /* Maximum number of bytes to Write during an I2C Write cycle (FieldsPeak RX FIFO Size + Len + CRC) */
#define I2C_READ_DATA_LENGTH 288 /* Number of bytes to Read during an I2C Read cycle (FieldsPeak TX FIFO Size + Len + CRC) */
#define I2C_LENGTH_FRAME_SIZE 5
#define ENTER() \
pr_debug("%s\n", __FUNCTION__)
/* The normal driver driver sequence is:
- open
- ... reset / read / write / poll
- close
*/
enum custom_state {
CUSTOM_INIT = 0,
CUSTOM_PROBED,
CUSTOM_OPENED,
};
#define RCV_BUFFER_NB 2
#define IOH_PHONE_ON 0
#define IOH_PHONE_OFF 1
#define RST_RESET 0
#define RST_NO_RESET 1
/* Context variable */
struct fdp_custom_device {
/* configuration stuff */
enum custom_state state;
/* mutex for concurrency */
struct mutex mutex;
/* I2C Driver related stuff */
struct i2c_client *i2c_client; /* I2C Driver registering structure */
unsigned int rst_gpio;
unsigned int irq_gpio;
unsigned int irqout;
/* I2C receiver */
uint16_t next_receive_length;
uint8_t next_to_read;
uint8_t next_to_write;
uint8_t rx_buffer[RCV_BUFFER_NB][I2C_READ_DATA_LENGTH];
uint16_t rx_data_length[RCV_BUFFER_NB];
uint8_t rx_scratch_buffer[I2C_READ_DATA_LENGTH];
/* process synchronization (poll) */
wait_queue_head_t read_queue;
};
static struct fdp_custom_device *fdp_p_device = NULL;
/**
* Function to initialize the state of the driver for new session
*
* It flushes all received data
* This function should be called each time a new session is done, e.g.
* - after initial opening of the device
* - when the FieldsPeak is reset
*/
static void fdp_struct_initialize(struct fdp_custom_device *p_device)
{
int i;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_struct_initialize: Internal error p_device is missing\n");
return;
}
p_device->next_receive_length = I2C_LENGTH_FRAME_SIZE;
p_device->next_to_read = 0;
p_device->next_to_write = 0;
for (i=0; i<RCV_BUFFER_NB; i++)
{
p_device->rx_data_length[i] = 0;
}
}
/**
* Function to cleanup the state of the driver
*
* Reverts to CUSTOM_PROBED state.
*
* This function should be called when the device is closed.
*/
static void fdp_struct_cleanup(struct fdp_custom_device *p_device)
{
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_struct_cleanup: Internal error p_device is missing\n");
return;
}
if (p_device->state == CUSTOM_OPENED) { /* it is opened */
p_device->state = CUSTOM_PROBED;
}
}
/**
* Function used to reset the chip
*
* Prefered method is to use RESET, fallback to REFIOH
*
*/
static void fdp_reset(struct fdp_custom_device *p_device)
{
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_update_stack_state: Internal error p_device is missing\n");
return;
}
if (gpio_is_valid(p_device->rst_gpio))
{
/* Reset RST/WakeUP for at least 2 micro-second */
gpio_set_value(p_device->rst_gpio, RST_RESET);
udelay(2);
gpio_set_value(p_device->rst_gpio, RST_NO_RESET);
}
}
/**
* Function used to initialize stack state management.
*
* Prefered method is to use REFIOH, fallback to RESET
*
*/
static int fdp_initialize_stack_state(struct fdp_custom_device *p_device)
{
int rc = 0;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_initialize_stack_state: Internal error p_device is missing\n");
return -1;
}
if (gpio_is_valid(p_device->rst_gpio)) {
rc = gpio_direction_output(p_device->rst_gpio, RST_NO_RESET);
}
return rc;
}
/**
* Function used to update stack state.
*
* Prefered method is to use REFIOH, fallback to RESET
*
*/
static void fdp_update_stack_state(struct fdp_custom_device *p_device, uint8_t active)
{
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_update_stack_state: Internal error p_device is missing\n");
return;
}
}
/**
* Function called when the user opens /dev/nfcc
*
* @return 0 on success, a negative value on failure.
*/
int fdp_custom_open(struct inode *inode, struct file *filp)
{
struct fdp_custom_device *p_device = fdp_p_device;
int retval = 0;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_custom_open: Internal error p_device is missing\n");
return -ENODEV;
}
mutex_lock(&p_device->mutex);
if (p_device->state == CUSTOM_INIT) {
printk(KERN_ERR "I2C controller was not probed, unable to send i2c data to the FDP!\n");
retval = -ENODEV;
goto end;
}
if (p_device->state == CUSTOM_OPENED) {
printk(KERN_ERR "The device is already opened\n");
retval = -EBUSY;
goto end;
}
filp->private_data = p_device;
/* Initialize the context for a new session */
fdp_struct_initialize(p_device);
p_device->state = CUSTOM_OPENED;
/* Inform the chip that the stack is active */
fdp_update_stack_state(p_device, 1);
end:
mutex_unlock(&p_device->mutex);
return retval;
}
/**
* Function called when the user reads data
*
* @return 0 on success, a negative value on failure.
*/
ssize_t fdp_custom_read(struct file * filp, char __user * buf, size_t count, loff_t * f_pos)
{
struct fdp_custom_device *p_device = filp->private_data;
ssize_t retval;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_custom_read: Internal error p_device is missing\n");
return -ENODEV;
}
mutex_lock(&p_device->mutex);
if (!p_device->rx_data_length[p_device->next_to_read]) {
/* no data available */
retval = -EAGAIN;
goto end;
}
/* we have data to read in p_device->rx_buffer[p_device->next_to_read] */
if (count < (p_device->rx_data_length[p_device->next_to_read] - 3)) {
/* supplied buffer is too small */
printk(KERN_ERR "fdp_custom_read : provided buffer too short.\n");
retval = -ENOSPC;
goto end;
}
if (copy_to_user(buf, & p_device->rx_buffer[p_device->next_to_read][2], p_device->rx_data_length[p_device->next_to_read] - 3)) {
printk(KERN_ERR "fdp_custom_read : unable to access to user buffer.\n");
retval = -EFAULT;
goto end;
}
retval = p_device->rx_data_length[p_device->next_to_read] - 3;
p_device->rx_data_length[p_device->next_to_read] = 0;
if (p_device->next_to_read < (RCV_BUFFER_NB - 1))
{
p_device->next_to_read++;
}
else
{
p_device->next_to_read = 0;
}
end:
mutex_unlock(&p_device->mutex);
return retval;
}
/**
* Function called when the user writes data
*
* @return 0 on success, a negative value on failure.
*/
ssize_t fdp_custom_write(struct file * filp, const char __user * buf, size_t count, loff_t * f_pos)
{
struct fdp_custom_device *p_device = filp->private_data;
int retval;
uint8_t tmpbuf[I2C_WRITE_DATA_LENGTH];
int i;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_custom_write: Internal error p_device is missing\n");
return -ENODEV;
}
if (count > I2C_WRITE_DATA_LENGTH - 3)
{
printk(KERN_ERR "fdp_custom_write: buffer too long\n");
return -EFAULT;
}
tmpbuf[0] = count >> 8;
tmpbuf[1] = count & 0xFF;
if (copy_from_user(&tmpbuf[2], buf, count) != 0) {
printk(KERN_ERR "fdp_custom_write : copy_from_user failed\n");
retval = -EFAULT;
goto end;
}
tmpbuf[2 + count] = 0;
for (i=0; i<2+count; i++)
{
tmpbuf[2 + count] ^= tmpbuf[i];
}
mutex_lock(&p_device->mutex);
retval = i2c_master_send(p_device->i2c_client, tmpbuf, count + 3);
if (retval != count + 3) {
printk(KERN_ERR "fdp_custom_write : i2c_master_send() failed (returns %d)\n", retval);
retval = -EFAULT;
}
mutex_unlock(&p_device->mutex);
end:
return retval;
}
/**
* Processes the OPEN_NFC_IOC_RESET ioctl
*
* @return 0 on success, a negative value on failure.
*/
long fdp_custom_ioctl_reset(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct fdp_custom_device *p_device = filp->private_data;
int rc = 0;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_custom_ioctl_reset: Internal error p_device is missing\n");
return -ENODEV;
}
disable_irq(p_device->irqout);
mutex_lock(&p_device->mutex);
fdp_reset(p_device);
/* Reinitialize context for a new session */
fdp_struct_initialize(p_device);
enable_irq(p_device->irqout);
mutex_unlock(&p_device->mutex);
return rc;
}
/**
* Process the poll()
*
* @return the poll status
*/
unsigned int fdp_custom_poll(struct file *filp, poll_table * wait)
{
struct fdp_custom_device *p_device = filp->private_data;
unsigned int mask = 0;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_custom_poll: Internal error p_device is missing\n");
return -ENODEV;
}
/* We accept the function be called in all states */
poll_wait(filp, &p_device->read_queue, wait);
mutex_lock(&p_device->mutex);
if (p_device->rx_data_length[p_device->next_to_read]) {
mask |= POLLIN | POLLRDNORM;
}
mutex_unlock(&p_device->mutex);
return mask;
}
/**
* Function called when the user closes file
*
* @return 0 on success, a negative value on failure.
*/
int fdp_custom_release(struct inode *inode, struct file *filp)
{
struct fdp_custom_device *p_device = filp->private_data;
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_custom_release: Internal error p_device is missing\n");
return -ENODEV;
}
mutex_lock(&p_device->mutex);
/* Inform the chip the stack is no longer active */
fdp_update_stack_state(p_device, 0);
/* Go back to PROBED state */
fdp_struct_cleanup(p_device);
mutex_unlock(&p_device->mutex);
return 0;
}
/**
* Perform an I2C Read cycle, upon IRQOUT.
*
* @return void
*/
static void fdp_irqout_read(struct fdp_custom_device *p_device)
{
int rc = 0, i;
uint8_t lrc;
uint8_t * p_buffer;
/* Do NOT interrupt an outgoing WRITE cycle */
mutex_lock(&p_device->mutex);
while (gpio_get_value(p_device->irq_gpio)) {
/* IRQ is high, FDP has some data for us */
if ((p_device->state == CUSTOM_OPENED) && (! p_device->rx_data_length[p_device->next_to_write]))
{
p_buffer = p_device->rx_buffer[p_device->next_to_write];
}
else
{
/* The buffer pool is full - use scratch buffer */
p_buffer = p_device->rx_scratch_buffer;
}
rc = i2c_master_recv(p_device->i2c_client, p_buffer, p_device->next_receive_length);
if (rc != p_device->next_receive_length) {
/* We did not received the amount of bytes expected.
* This is very strange, since FDP should never NAK on reception
* A transmission error during I2C ACK bit may explain this situation
* ... */
printk(KERN_ERR "fdp_irqout_read: i2c_master_recv() failed (returns %d)\n", rc);
p_device->next_receive_length = 5;
continue;
}
/* Check the received I2C paquet integrity */
for (lrc=i=0; i<rc; i++)
lrc ^= p_buffer[i];
if (lrc) {
/* LRC check fails.
* This may due to transmission error or desynchronization between driver and FDP.
* Drop the paquet and force resynchronization */
printk(KERN_ERR "fdp_irqout_read: corrupted packet\n");
p_device->next_receive_length = 5;
continue;
}
/* All is fine, process the received paquet */
if ( (p_buffer[0] == 0x00) && (p_buffer[1] == 0x00)) {
/* We received a I2C frame that carries a length */
/* Compute the next I2C packet size */
p_device->next_receive_length = (p_buffer[2] << 8) + p_buffer[3] + 3;
}
else {
/* We received a I2C data */
/* Next expected packet is a I2C length */
p_device->next_receive_length = 5;
if ( ((p_buffer[0]<< 8) + p_buffer[1]) != (rc - 3)) {
/* Length specified in the data packet does not match the expected length
* This is very strange. */
printk(KERN_ERR "fdp_irqout_read : unexpected data frame length");
continue;
}
if (p_buffer != p_device->rx_scratch_buffer)
{
/* store received data length for user read */
p_device->rx_data_length[p_device->next_to_write] = rc;
if (p_device->next_to_write < (RCV_BUFFER_NB - 1))
{
p_device->next_to_write++;
}
else
{
p_device->next_to_write = 0;
}
/* wake up poll() */
wake_up(&p_device->read_queue);
}
}
};
mutex_unlock(&p_device->mutex);
}
/**
* IRQOUT Interrupt handler.
* @return the IRQ processing status
*/
static irqreturn_t fdp_i2c_irq_thread_fn(int irq, void *dev_id)
{
struct fdp_custom_device *p_device = fdp_p_device;
if (!p_device) {
printk(KERN_ERR "fdp_i2c_interrupt: Internal error p_device is missing, disabing the IRQ\n");
} else {
fdp_irqout_read(p_device);
}
return IRQ_HANDLED;
}
/**
* Device/Driver binding: probe
*
* @return 0 if successfull, or error code
*/
static int fdp_probe(struct i2c_client *client, const struct i2c_device_id *idp)
{
int rc = 0;
struct fdp_custom_device *p_device = fdp_p_device;
struct fdp_i2c_platform_data * p_platform_data = dev_get_platdata(&client->dev);
printk (KERN_INFO "fdp_probe: IRQ %d", client->irq);
ENTER();
if (!p_device) {
printk(KERN_ERR "fdp_probe: Internal error p_device is missing\n");
return -ENODEV;
}
if (!p_platform_data) {
printk(KERN_ERR "fdp_probe: missing platform data");
return -EINVAL;
}
mutex_lock(&p_device->mutex);
if (p_device->state != CUSTOM_INIT) {
rc = EEXIST;
goto unlock;
}
p_device->i2c_client = client;
i2c_set_clientdata(client, p_device);
rc = gpio_request(p_platform_data->irq_gpio, NFC_HOST_INT_GPIO);
if (rc) {
dev_err(&client->dev, "Request NFC IRQOUT GPIO fails %d\n", rc);
rc = -ENODEV;
goto unlock;
}
rc = gpio_direction_input(p_platform_data->irq_gpio);
if (rc) {
dev_err(&client->dev, "Set IRQ GPIO direction fails %d\n", rc);
rc = -ENODEV;
goto err_int_gpio;
}
/* Map IRQ nb to GPIO id */
client->irq = gpio_to_irq(p_platform_data->irq_gpio);
rc = gpio_request(p_platform_data->rst_gpio, NFC_RESET_GPIO);
if (rc) {
dev_err(&client->dev,
"Request for NFC Reset GPIO fails %d\n", rc);
rc = -ENODEV;
goto err_int_gpio;
}
rc = gpio_direction_output(p_platform_data->rst_gpio, RST_RESET);
if (rc) {
dev_err(&client->dev, "Set Reset GPIO direction fails %d\n", rc);
rc = -ENODEV;
goto err_rst_gpio;
}
p_device->rst_gpio = p_platform_data->rst_gpio;
p_device->irq_gpio = p_platform_data->irq_gpio;
printk(KERN_INFO "RST IRQ gpios %d %d\n", p_device->rst_gpio,
p_device->irq_gpio);
/* Phone ON/OFF management */
rc = fdp_initialize_stack_state(p_device);
if (rc < 0)
{
printk(KERN_ERR "fail to initialize stack state management\n");
goto unlock;
}
p_device->irqout = client->irq;
p_device->next_receive_length = I2C_LENGTH_FRAME_SIZE;
rc = request_threaded_irq(client->irq, NULL, fdp_i2c_irq_thread_fn, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "IRQOUT_input", p_device);
if (rc < 0) {
printk(KERN_ERR "fdp_probe : failed to register IRQOUT\n");
goto unlock;
}
/* enable to wake the device using this IRQ */
rc = irq_set_irq_wake(client->irq, 1);
if (rc < 0) {
printk(KERN_ERR "fdp_probe : failed to set irq as wake up source");
}
/* The IRQ can be trigged here at most once, because we are holding the mutex (cannot be re-enabled after disable) */
p_device->state = CUSTOM_PROBED;
err_rst_gpio:
gpio_free(p_platform_data->rst_gpio);
err_int_gpio:
gpio_free(p_platform_data->irq_gpio);
unlock:
mutex_unlock(&p_device->mutex);
return rc;
}
int fdp_suspend(struct i2c_client * client, pm_message_t mesg)
{
struct fdp_custom_device *p_device = fdp_p_device;
printk(KERN_INFO "fdp_suspend\n");
if (! p_device)
{
printk(KERN_ERR "fdp_suspend: Internal error p_device is missing\n");
return 0;
}
if (p_device->state == CUSTOM_OPENED)
{
//disable_irq(p_device->irqout);
}
return 0;
}
int fdp_resume(struct i2c_client * client)
{
struct fdp_custom_device *p_device = fdp_p_device;
printk(KERN_INFO "fdp_resume\n");
if (! p_device)
{
printk(KERN_ERR "fdp_resume: Internal error p_device is missing\n");
return 0;
}
if (p_device->state == CUSTOM_OPENED)
{
//enable_irq(p_device->irqout);
}
return 0;
}
/**
* Device/Driver binding: remove
*
* @return 0 if successfull, or error code
*/
static int fdp_remove(struct i2c_client *client)
{
struct fdp_custom_device *p_device;
ENTER();
p_device = fdp_p_device;
/* disable to wake the device using this IRQ */
irq_set_irq_wake(p_device->irqout, 0);
/* free the IRQ */
free_irq(p_device->irqout, p_device);
if (gpio_is_valid(p_device->irq_gpio))
gpio_free(p_device->irq_gpio);
if (gpio_is_valid(p_device->rst_gpio))
gpio_free(p_device->rst_gpio);
if (p_device && (p_device->state >= CUSTOM_PROBED)) {
i2c_set_clientdata(p_device->i2c_client, NULL);
}
p_device->state = CUSTOM_INIT;
return 0;
}
/*
Client structure holds device-specific information like the
driver model device node, and its I2C address
*/
static const struct i2c_device_id fdp_id[] = {
{ I2C_ID_NAME, I2C_DEVICE_ADDR },
{ }
};
MODULE_DEVICE_TABLE(i2c, fdp_id);
static struct i2c_driver fdp_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = I2C_ID_NAME,
},
.probe = fdp_probe,
.remove = __devexit_p(fdp_remove),
.suspend = fdp_suspend,
.resume = fdp_resume,
.id_table = fdp_id,
};
/**
* Specific initialization, when driver module is inserted.
*
* @return 0 if successfull, or error code
*/
int
fdp_custom_init(void)
{
int retval = 0;
struct fdp_custom_device *p_device;
ENTER();
p_device = kmalloc(sizeof(struct fdp_custom_device), GFP_KERNEL);
if (p_device == NULL) {
return -ENOMEM;
}
memset(p_device, 0, sizeof(struct fdp_custom_device));
init_waitqueue_head(&p_device->read_queue);
mutex_init(&p_device->mutex);
/* Save device context (needed in .probe()) */
fdp_p_device = p_device;
retval = i2c_add_driver(&fdp_i2c_driver);
if (retval < 0) {
printk(KERN_ERR "fdp_custom_init : failed to add I2C driver\n");
goto end;
}
printk(KERN_INFO "fdp driver loaded\n");
end:
return retval;
}
/**
* Specific cleanup, when driver module is removed.
*
* @return void
*/
void fdp_custom_exit(void)
{
struct fdp_custom_device *p_device;
ENTER();
p_device = fdp_p_device;
if (p_device == NULL)
return;
i2c_del_driver(&fdp_i2c_driver);
mutex_destroy(&p_device->mutex);
/* free the custom device context */
kfree(p_device);
fdp_p_device = NULL;
}
/* EOF */