blob: 285f4ba9a975b40d153b8653afd9a578b28f5617 [file] [log] [blame]
/*
* Copyright (C) 2016 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/i2c.h>
#include <linux/iio/iio.h>
#include "main.h"
#include "bl.h"
#include "comms.h"
static uint8_t bl_checksum(const uint8_t *bytes, int length)
{
int i;
uint8_t csum;
if (length == 1) {
csum = ~bytes[0];
} else if (length > 1) {
for (csum = 0, i = 0; i < length; i++)
csum ^= bytes[i];
} else {
csum = 0xFF;
}
return csum;
}
static uint8_t i2c_bl_write_data(const struct nanohub_bl *bl, uint8_t *buffer,
int length)
{
buffer[length] = bl_checksum(buffer, length);
if (i2c_master_send(bl->i2c_client, buffer, length + 1) == (length + 1))
return CMD_ACK;
else
return CMD_NACK;
}
static uint8_t i2c_bl_write_cmd(const struct nanohub_bl *bl, uint8_t cmd)
{
uint8_t buffer[sizeof(uint8_t) + 1] = {
cmd
};
return bl->write_data(bl, buffer, sizeof(uint8_t));
}
static uint8_t i2c_bl_read_data(const struct nanohub_bl *bl, uint8_t *data,
int length)
{
if (i2c_master_recv(bl->i2c_client, data, length) == length)
return CMD_ACK;
else
return CMD_NACK;
}
static uint8_t i2c_bl_read_ack(const struct nanohub_bl *bl)
{
uint8_t buffer;
if (bl->read_data(bl, &buffer, sizeof(uint8_t)) == CMD_ACK)
return buffer;
else
return CMD_NACK;
}
static void i2c_bl_open(const struct nanohub_bl *bl)
{
}
static void i2c_bl_close(const struct nanohub_bl *bl)
{
}
static uint8_t i2c_bl_sync(const struct nanohub_bl *bl)
{
return CMD_ACK;
}
void nanohub_i2c_bl_init(struct nanohub_bl *bl)
{
bl->open = i2c_bl_open;
bl->sync = i2c_bl_sync;
bl->write_data = i2c_bl_write_data;
bl->write_cmd = i2c_bl_write_cmd;
bl->read_data = i2c_bl_read_data;
bl->read_ack = i2c_bl_read_ack;
bl->close = i2c_bl_close;
}
int nanohub_i2c_write(const struct nanohub_comms *comms, uint8_t *data,
int length)
{
return i2c_master_send(comms->i2c_client, data, length);
}
int nanohub_i2c_read(struct nanohub_comms *comms, uint8_t *data,
int max_length, int timeout)
{
const int min_size = sizeof(struct nanohub_packet) +
sizeof(struct nanohub_packet_crc);
int i, ret, read_cnt = min_size, read_len = 0, read_total = 0;
struct nanohub_packet *packet;
if (max_length < min_size)
return ERROR_NACK;
do {
pr_debug
("master_recv: read_len=%d, read_cnt=%d, read_total=%d\n",
read_len, read_cnt, read_total);
ret =
i2c_master_recv(comms->i2c_client, &data[read_len],
read_cnt);
pr_debug("master_recv: ret=%d\n", ret);
pr_debug(
"data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
data[0], data[1], data[2], data[3], data[4], data[5],
data[6], data[7], data[8], data[9], data[10], data[11],
data[12], data[13], data[14], data[15], data[16], data[17],
data[18], data[19], data[20], data[21], data[22]);
if (ret == read_cnt) {
if (read_len == 0) {
for (i = 0; i < ret; i++) {
if (data[i] != 0xFF) {
if (i > 0) {
pr_debug(
"i=%d, read_cnt - i = %d\n",
i, read_cnt - i);
memmove(data, &data[i],
read_cnt - i);
}
read_len = read_cnt - i;
break;
} else {
timeout--;
}
}
} else {
read_len += read_cnt;
}
packet = (struct nanohub_packet *)data;
if (read_len >= offsetof(struct nanohub_packet, len) +
sizeof(packet->len)) {
read_total = packet->len + min_size;
read_cnt = read_total - read_len;
} else {
read_cnt = min_size - read_len;
}
} else {
break;
}
if (read_cnt + read_len > max_length) {
ret = ERROR_NACK;
break;
}
} while (timeout > 0 && (read_total == 0 || read_total != read_len));
if (ret < 0)
return ret;
else if (timeout <= 0)
return 0;
else
return read_len;
}
static void nanohub_i2c_open(const struct nanohub_comms *comms)
{
}
static void nanohub_i2c_close(const struct nanohub_comms *comms)
{
}
void nanohub_i2c_comms_init(struct nanohub_comms *comms)
{
comms->timeout_ack = 128;
comms->timeout_reply = 512;
comms->open = nanohub_i2c_open;
comms->close = nanohub_i2c_close;
comms->write = nanohub_i2c_write;
comms->read = nanohub_i2c_read;
}
static int nanohub_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct nanohub_data *data;
struct iio_dev *iio_dev;
pr_info("nanohub: i2c_probe:\n");
data = nanohub_probe(&client->dev);
if (IS_ERR(data))
return PTR_ERR(data);
iio_dev = iio_priv_to_dev(data);
i2c_set_clientdata(client, iio_dev);
data->comms.i2c_client = client;
nanohub_i2c_comms_init(&data->comms);
data->bl.cmd_erase = CMD_ERASE_NS;
data->bl.cmd_read_memory = CMD_READ_MEMORY;
data->bl.cmd_write_memory = CMD_WRITE_MEMORY_NS;
data->bl.i2c_client = i2c_new_dummy(client->adapter, 0x39);
i2c_set_clientdata(data->bl.i2c_client, iio_dev);
nanohub_i2c_bl_init(&data->bl);
return 0;
}
static int nanohub_i2c_remove(struct i2c_client *client)
{
struct nanohub_data *data;
data = iio_priv(i2c_get_clientdata(client));
i2c_unregister_device(data->bl.i2c_client);
return nanohub_remove(data);
}
static struct i2c_device_id nanohub_i2c_id[] = {
{"nanohub", 0},
{},
};
static struct i2c_driver nanohub_i2c_driver = {
.driver = {
.name = "nanohub",
},
.probe = nanohub_i2c_probe,
.remove = nanohub_i2c_remove,
.id_table = nanohub_i2c_id,
};
static const struct iio_info nanohub_iio_info = {
.driver_module = THIS_MODULE,
};
int __init nanohub_i2c_init(void)
{
return i2c_add_driver(&nanohub_i2c_driver);
}
void nanohub_i2c_cleanup(void)
{
i2c_del_driver(&nanohub_i2c_driver);
}
MODULE_DEVICE_TABLE(i2c, nanohub_i2c_id);