/* * Copyright (C) 2016 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 <errno.h> #include <heap.h> #include <string.h> #include <cpu.h> #include <spi.h> #include <spi_priv.h> #include <timer.h> #define INFO_PRINT(fmt, ...) do { \ osLog(LOG_INFO, "%s " fmt, "[spi]", ##__VA_ARGS__); \ } while (0); #define ERROR_PRINT(fmt, ...) do { \ osLog(LOG_ERROR, "%s " fmt, "[spi] ERROR:", ##__VA_ARGS__); \ } while (0); struct SpiDeviceState { struct SpiDevice dev; const struct SpiPacket *packets; size_t n; size_t currentBuf; struct SpiMode mode; uint16_t tid; SpiCbkF rxTxCallback; void *rxTxCookie; SpiCbkF finishCallback; void *finishCookie; int err; }; #define SPI_DEVICE_TO_STATE(p) ((struct SpiDeviceState *)p) static void spiMasterNext(struct SpiDeviceState *state); static void spiMasterStop(struct SpiDeviceState *state); static void spiMasterDone(struct SpiDeviceState *state, int err); static void spiSlaveNext(struct SpiDeviceState *state); static void spiSlaveIdle(struct SpiDeviceState *state, int err); static void spiSlaveDone(struct SpiDeviceState *state); static int spiMasterStart(struct SpiDeviceState *state, spi_cs_t cs, const struct SpiMode *mode) { struct SpiDevice *dev = &state->dev; if (dev->ops->masterStartAsync) return dev->ops->masterStartAsync(dev, cs, mode); if (dev->ops->masterStartSync) { int err = dev->ops->masterStartSync(dev, cs, mode); if (err < 0) return err; } return dev->ops->masterRxTx(dev, state->packets[0].rxBuf, state->packets[0].txBuf, state->packets[0].size, mode); } void spi_masterStartAsync_done(struct SpiDevice *dev, int err) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); if (err) spiMasterDone(state, err); else spiMasterNext(state); } static void spiDelayCallback(uint32_t timerId, void *data) { spiMasterNext((struct SpiDeviceState *)data); } static void spiMasterNext(struct SpiDeviceState *state) { struct SpiDevice *dev = &state->dev; if (state->currentBuf == state->n) { spiMasterStop(state); return; } size_t i = state->currentBuf; void *rxBuf = state->packets[i].rxBuf; const void *txBuf = state->packets[i].txBuf; size_t size = state->packets[i].size; const struct SpiMode *mode = &state->mode; int err = dev->ops->masterRxTx(dev, rxBuf, txBuf, size, mode); if (err) spiMasterDone(state, err); } void spiMasterRxTxDone(struct SpiDevice *dev, int err) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); if (err) { spiMasterDone(state, err); } else { size_t i = state->currentBuf++; if (state->packets[i].delay > 0) { if (!timTimerSet(state->packets[i].delay, 0, 50, spiDelayCallback, state, true)) { ERROR_PRINT("Cannot do delayed spi, timer depleted\n"); spiMasterDone(state, -ENOMEM); // should be out of timer; out of mem is close enough } } else { spiMasterNext(state); } } } static void spiMasterStop(struct SpiDeviceState *state) { struct SpiDevice *dev = &state->dev; if (dev->ops->masterStopSync) { int err = dev->ops->masterStopSync(dev); spiMasterDone(state, err); } else if (dev->ops->masterStopAsync) { int err = dev->ops->masterStopAsync(dev); if (err < 0) spiMasterDone(state, err); } else { spiMasterDone(state, 0); } } void spiMasterStopAsyncDone(struct SpiDevice *dev, int err) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); spiMasterDone(state, err); } static void spiMasterDone(struct SpiDeviceState *state, int err) { SpiCbkF callback = state->rxTxCallback; void *cookie = state->rxTxCookie; uint16_t oldTid = osSetCurrentTid(state->tid); callback(cookie, err); osSetCurrentTid(oldTid); } static int spiSlaveStart(struct SpiDeviceState *state, const struct SpiMode *mode) { struct SpiDevice *dev = &state->dev; if (dev->ops->slaveStartAsync) return dev->ops->slaveStartAsync(dev, mode); if (dev->ops->slaveStartSync) { int err = dev->ops->slaveStartSync(dev, mode); if (err < 0) return err; } return dev->ops->slaveIdle(dev, mode); } void spiSlaveStartAsyncDone(struct SpiDevice *dev, int err) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); if (err) state->err = err; else state->err = dev->ops->slaveIdle(dev, &state->mode); } void spiSlaveRxTxDone(struct SpiDevice *dev, int err) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); if (err) { spiSlaveIdle(state, err); } else { state->currentBuf++; spiSlaveNext(state); } } void spiSlaveCsInactive(struct SpiDevice *dev) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); dev->ops->slaveSetCsInterrupt(dev, false); if (!state->finishCallback) { osLog(LOG_WARN, "%s called without callback\n", __func__); return; } SpiCbkF callback = state->finishCallback; void *cookie = state->finishCookie; state->finishCallback = NULL; state->finishCookie = NULL; uint16_t oldTid = osSetCurrentTid(state->tid); callback(cookie, 0); osSetCurrentTid(oldTid); } static void spiSlaveNext(struct SpiDeviceState *state) { struct SpiDevice *dev = &state->dev; if (state->currentBuf == state->n) { spiSlaveIdle(state, 0); return; } size_t i = state->currentBuf; void *rxBuf = state->packets[i].rxBuf; const void *txBuf = state->packets[i].txBuf; size_t size = state->packets[i].size; const struct SpiMode *mode = &state->mode; int err = dev->ops->slaveRxTx(dev, rxBuf, txBuf, size, mode); if (err) spiSlaveIdle(state, err); } static void spiSlaveIdle(struct SpiDeviceState *state, int err) { struct SpiDevice *dev = &state->dev; SpiCbkF callback = state->rxTxCallback; void *cookie = state->rxTxCookie; if (!err) err = dev->ops->slaveIdle(dev, &state->mode); uint16_t oldTid = osSetCurrentTid(state->tid); callback(cookie, err); osSetCurrentTid(oldTid); } void spiSlaveStopAsyncDone(struct SpiDevice *dev, int err) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); spiSlaveDone(state); } static void spiSlaveDone(struct SpiDeviceState *state) { struct SpiDevice *dev = &state->dev; if (dev->ops->release) dev->ops->release(dev); heapFree(state); } static int spiSetupRxTx(struct SpiDeviceState *state, const struct SpiPacket packets[], size_t n, SpiCbkF callback, void *cookie) { state->packets = packets; state->n = n; state->currentBuf = 0; state->rxTxCallback = callback; state->rxTxCookie = cookie; state->tid = osGetCurrentTid(); return 0; } int spiMasterRequest(uint8_t busId, struct SpiDevice **dev_out) { int ret = 0; struct SpiDeviceState *state = heapAlloc(sizeof(*state)); if (!state) return -ENOMEM; struct SpiDevice *dev = &state->dev; ret = spiRequest(dev, busId); if (ret < 0) goto err_request; if (!dev->ops->masterRxTx) { ret = -EOPNOTSUPP; goto err_opsupp; } *dev_out = dev; return 0; err_opsupp: if (dev->ops->release) dev->ops->release(dev); err_request: heapFree(state); return ret; } int spiMasterRxTx(struct SpiDevice *dev, spi_cs_t cs, const struct SpiPacket packets[], size_t n, const struct SpiMode *mode, SpiCbkF callback, void *cookie) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); int ret = 0; if (!n) return -EINVAL; ret = spiSetupRxTx(state, packets, n, callback, cookie); if (ret < 0) return ret; state->mode = *mode; return spiMasterStart(state, cs, mode); } int spiMasterRelease(struct SpiDevice *dev) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); if (dev->ops->release) { int ret = dev->ops->release(dev); if (ret < 0) return ret; } heapFree(state); return 0; } int spiSlaveRequest(uint8_t busId, const struct SpiMode *mode, struct SpiDevice **dev_out) { int ret = 0; struct SpiDeviceState *state = heapAlloc(sizeof(*state)); if (!state) return -ENOMEM; struct SpiDevice *dev = &state->dev; ret = spiRequest(dev, busId); if (ret < 0) goto err_request; if (!dev->ops->slaveIdle || !dev->ops->slaveRxTx) { ret = -EOPNOTSUPP; goto err_opsupp; } state->mode = *mode; state->err = 0; ret = spiSlaveStart(state, mode); if (ret < 0) goto err_opsupp; *dev_out = dev; return 0; err_opsupp: if (dev->ops->release) dev->ops->release(dev); err_request: heapFree(state); return ret; } int spiSlaveRxTx(struct SpiDevice *dev, const struct SpiPacket packets[], size_t n, SpiCbkF callback, void *cookie) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); if (!n) return -EINVAL; if (state->err) return state->err; int ret = spiSetupRxTx(state, packets, n, callback, cookie); if (ret < 0) return ret; return dev->ops->slaveRxTx(dev, state->packets[0].rxBuf, state->packets[0].txBuf, state->packets[0].size, &state->mode); } int spiSlaveWaitForInactive(struct SpiDevice *dev, SpiCbkF callback, void *cookie) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); if (!dev->ops->slaveSetCsInterrupt || !dev->ops->slaveCsIsActive) return -EOPNOTSUPP; state->finishCallback = callback; state->finishCookie = cookie; uint64_t flags = cpuIntsOff(); dev->ops->slaveSetCsInterrupt(dev, true); /* CS may already be inactive before enabling the interrupt. In this case * roll back and fire the callback immediately. * * Interrupts must be off while checking for this. Otherwise there is a * (very unlikely) race where the CS interrupt fires between calling * slaveSetCsInterrupt(true) and the rollback * slaveSetCsInterrupt(false), causing the event to be handled twice. * * Likewise the check must come after enabling the interrupt. Otherwise * there is an (also unlikely) race where CS goes inactive between reading * CS and enabling the interrupt, causing the event to be lost. */ bool cs = dev->ops->slaveCsIsActive(dev); if (!cs) { dev->ops->slaveSetCsInterrupt(dev, false); cpuIntsRestore(flags); state->finishCallback = NULL; state->finishCookie = NULL; callback(cookie, 0); return 0; } cpuIntsRestore(flags); return 0; } int spiSlaveRelease(struct SpiDevice *dev) { struct SpiDeviceState *state = SPI_DEVICE_TO_STATE(dev); int ret; if (dev->ops->slaveStopSync) { ret = dev->ops->slaveStopSync(dev); if (ret < 0) return ret; } else if (dev->ops->slaveStopAsync) { return dev->ops->slaveStopAsync(dev); } spiSlaveDone(state); return 0; }