/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include "spi.h"
uint8_t spi_write_data(handle_t *handle, uint8_t *buffer, int length)
{
spi_handle_t *spi_handle = (spi_handle_t *)handle;
struct spi_ioc_transfer xfer =
{
.len = length + 1,
.tx_buf = (unsigned long)buffer,
.rx_buf = (unsigned long)buffer,
.cs_change = 1,
};
buffer[length] = checksum(handle, buffer, length);
if (ioctl(spi_handle->fd, SPI_IOC_MESSAGE(1), &xfer) >= 0)
return buffer[length];
else
return CMD_NACK;
}
uint8_t spi_write_cmd(handle_t *handle, uint8_t cmd)
{
spi_handle_t *spi_handle = (spi_handle_t *)handle;
uint8_t buffer[] =
{
CMD_SOF,
cmd,
~cmd
};
struct spi_ioc_transfer xfer =
{
.len = sizeof(buffer),
.tx_buf = (unsigned long)buffer,
.rx_buf = (unsigned long)buffer,
.cs_change = 1,
};
if (ioctl(spi_handle->fd, SPI_IOC_MESSAGE(1), &xfer) >= 0)
return CMD_ACK;
else
return CMD_NACK;
}
uint8_t spi_read_data(handle_t *handle, uint8_t *data, int length)
{
spi_handle_t *spi_handle = (spi_handle_t *)handle;
uint8_t buffer[] =
{
0x00
};
struct spi_ioc_transfer xfer[] =
{
{
.len = sizeof(buffer),
.tx_buf = (unsigned long)buffer,
.rx_buf = (unsigned long)buffer,
},
{
.len = length,
.tx_buf = (unsigned long)data,
.rx_buf = (unsigned long)data,
.cs_change = 1,
}
};
if (ioctl(spi_handle->fd, SPI_IOC_MESSAGE(2), xfer) >= 0)
return CMD_ACK;
else
return CMD_NACK;
}
uint8_t spi_read_ack(handle_t *handle)
{
spi_handle_t *spi_handle = (spi_handle_t *)handle;
uint16_t timeout = 65535;
uint8_t ret;
uint8_t buffer[] =
{
0x00,
};
struct spi_ioc_transfer xfer =
{
.len = sizeof(buffer),
.tx_buf = (unsigned long)buffer,
.rx_buf = (unsigned long)buffer,
.cs_change = 1,
};
if (ioctl(spi_handle->fd, SPI_IOC_MESSAGE(1), &xfer) >= 0) {
do {
ioctl(spi_handle->fd, SPI_IOC_MESSAGE(1), &xfer);
timeout --;
} while (buffer[0] != CMD_ACK && buffer[0] != CMD_NACK && timeout > 0);
if (buffer[0] != CMD_ACK && buffer[0] != CMD_NACK && timeout == 0)
ret = CMD_NACK;
else
ret = buffer[0];
ioctl(spi_handle->fd, SPI_IOC_MESSAGE(1), &xfer);
return ret;
} else {
return CMD_NACK;
}
}
uint8_t spi_sync(handle_t *handle)
{
spi_handle_t *spi_handle = (spi_handle_t *)handle;
uint8_t buffer[] =
{
CMD_SOF,
};
struct spi_ioc_transfer xfer =
{
.len = sizeof(buffer),
.tx_buf = (unsigned long)buffer,
.rx_buf = (unsigned long)buffer,
.cs_change = 1,
};
if (ioctl(spi_handle->fd, SPI_IOC_MESSAGE(1), &xfer) >= 0)
return handle->read_ack(handle);
else
return CMD_NACK;
}
int spi_init(handle_t *handle)
{
spi_handle_t *spi_handle = (spi_handle_t *)handle;
uint8_t tmp8;
uint32_t tmp32;
handle->cmd_erase = CMD_ERASE;
handle->cmd_read_memory = CMD_READ_MEMORY;
handle->cmd_write_memory = CMD_WRITE_MEMORY;
handle->write_data = spi_write_data;
handle->write_cmd = spi_write_cmd;
handle->read_data = spi_read_data;
handle->read_ack = spi_read_ack;
tmp8 = SPI_MODE_0;
if (ioctl(spi_handle->fd, SPI_IOC_WR_MODE, &tmp8) < 0) {
perror("Error setting mode");
return -1;
}
tmp32 = 8000000;
if (ioctl(spi_handle->fd, SPI_IOC_WR_MAX_SPEED_HZ, &tmp32) < 0) {
perror("Error setting speed");
return -1;
}
tmp8 = 8;
if (ioctl(spi_handle->fd, SPI_IOC_WR_BITS_PER_WORD, &tmp8) < 0) {
perror("Error setting bits per word");
return -1;
}
if (spi_sync(handle) == CMD_ACK)
return 0;
else
return -1;
}