| /* |
| * 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); |