#include "i2cnex.h"
void
i2c_txn_free(i2c_txn_t *txn)
{
if (txn == NULL) {
return;
}
VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_UNLOCKED);
i2c_ctrl_t *ctrl = txn->txn_ctrl;
mutex_enter(&ctrl->ic_txn_lock);
list_remove(&ctrl->ic_txns, txn);
mutex_exit(&ctrl->ic_txn_lock);
cv_destroy(&txn->txn_cv);
kmem_free(txn, sizeof (i2c_txn_t));
}
i2c_txn_t *
i2c_txn_alloc(i2c_ctrl_t *ctrl, i2c_txn_tag_t tag, const void *debug)
{
i2c_txn_t *txn = kmem_zalloc(sizeof (i2c_txn_t), KM_SLEEP);
txn->txn_tag = tag;
txn->txn_debug = debug;
txn->txn_alloc_kthread = (uintptr_t)curthread;
txn->txn_ctrl = ctrl;
cv_init(&txn->txn_cv, NULL, CV_DRIVER, NULL);
mutex_enter(&ctrl->ic_txn_lock);
list_insert_tail(&ctrl->ic_txns, txn);
mutex_exit(&ctrl->ic_txn_lock);
return (txn);
}
bool
i2c_txn_held(i2c_txn_t *txn)
{
if (txn == NULL) {
return (false);
}
i2c_ctrl_t *ctrl = txn->txn_ctrl;
i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
mutex_enter(&lock->cl_mutex);
bool ret = lock->cl_owner == txn;
mutex_exit(&lock->cl_mutex);
return (ret);
}
static void
i2c_txn_lock_acquire(i2c_ctrl_lock_t *lock, i2c_txn_t *txn)
{
VERIFY3P(lock->cl_owner, ==, NULL);
VERIFY3P(txn->txn_state, ==, I2C_TXN_STATE_UNLOCKED);
VERIFY3U(list_link_active(&txn->txn_wait_link), ==, 0);
VERIFY3P(&txn->txn_ctrl->ic_lock, ==, lock);
txn->txn_state = I2C_TXN_STATE_ACQUIRED;
txn->txn_last_change = gethrtime();
txn->txn_acq_kthread = (uintptr_t)curthread;
txn->txn_acq_pid = curproc->p_pid;
lock->cl_owner = txn;
lock->cl_nlocks++;
}
static void
i2c_txn_lock_wakeup(i2c_ctrl_t *ctrl)
{
i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
VERIFY(MUTEX_HELD(&lock->cl_mutex));
VERIFY3P(lock->cl_owner, ==, NULL);
i2c_txn_t *next = list_remove_head(&lock->cl_waiters);
if (next == NULL)
return;
next->txn_state = I2C_TXN_STATE_UNLOCKED;
next->txn_last_change = gethrtime();
i2c_txn_lock_acquire(lock, next);
cv_signal(&next->txn_cv);
}
static void
i2c_txn_lock_signal(i2c_ctrl_t *ctrl, i2c_txn_t *txn)
{
i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
VERIFY3U(txn->txn_state, !=, I2C_TXN_STATE_UNLOCKED);
VERIFY(MUTEX_HELD(&lock->cl_mutex));
txn->txn_last_change = gethrtime();
txn->txn_err = I2C_CORE_E_LOCK_WAIT_SIGNAL;
lock->cl_nsig++;
if (txn->txn_state == I2C_TXN_STATE_BLOCKED) {
VERIFY3U(lock->cl_owner, !=, txn);
VERIFY3U(list_link_active(&txn->txn_wait_link), !=, 0);
list_remove(&lock->cl_waiters, txn);
txn->txn_state = I2C_TXN_STATE_UNLOCKED;
lock->cl_nsig_block++;
return;
}
lock->cl_nsig_acq++;
i2c_txn_ctrl_unlock(txn);
}
void
i2c_txn_ctrl_unlock(i2c_txn_t *txn)
{
i2c_ctrl_t *ctrl = txn->txn_ctrl;
i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
mutex_enter(&lock->cl_mutex);
VERIFY3P(lock->cl_owner, ==, txn);
VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
txn->txn_last_change = gethrtime();
txn->txn_state = I2C_TXN_STATE_UNLOCKED;
lock->cl_owner = list_remove_head(&lock->cl_stack);
if (lock->cl_owner == NULL) {
i2c_txn_lock_wakeup(ctrl);
}
mutex_exit(&lock->cl_mutex);
}
i2c_errno_t
i2c_txn_ctrl_lock(i2c_txn_t *txn, bool block)
{
i2c_ctrl_t *ctrl = txn->txn_ctrl;
i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
hrtime_t sleep_time;
i2c_errno_t ret;
mutex_enter(&lock->cl_mutex);
VERIFY3P(lock->cl_owner, !=, txn);
if (lock->cl_owner == NULL) {
VERIFY3U(list_is_empty(&lock->cl_waiters), !=, 0);
i2c_txn_lock_acquire(lock, txn);
mutex_exit(&lock->cl_mutex);
return (I2C_CORE_E_OK);
}
if (lock->cl_nexus_thr == (uintptr_t)curthread) {
list_insert_head(&lock->cl_stack, lock->cl_owner);
lock->cl_owner = NULL;
i2c_txn_lock_acquire(lock, txn);
lock->cl_nstack++;
mutex_exit(&lock->cl_mutex);
return (I2C_CORE_E_OK);
}
if (!block) {
mutex_exit(&lock->cl_mutex);
return (I2C_CORE_E_LOCK_WOULD_BLOCK);
}
VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_UNLOCKED);
txn->txn_state = I2C_TXN_STATE_BLOCKED;
sleep_time = gethrtime();
list_insert_tail(&lock->cl_waiters, txn);
while (txn->txn_state == I2C_TXN_STATE_BLOCKED) {
if (cv_wait_sig(&txn->txn_cv, &lock->cl_mutex) == 0) {
i2c_txn_lock_signal(ctrl, txn);
break;
}
}
VERIFY3S(txn->txn_last_change, !=, sleep_time);
VERIFY3U(list_link_active(&txn->txn_wait_link), ==, 0);
if (txn->txn_state == I2C_TXN_STATE_UNLOCKED) {
ret = txn->txn_err;
} else {
VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
VERIFY3P(lock->cl_owner, ==, txn);
ret = I2C_CORE_E_OK;
}
mutex_exit(&lock->cl_mutex);
return (ret);
}
void
i2c_txn_nexus_op_end(i2c_txn_t *txn)
{
i2c_ctrl_t *ctrl = txn->txn_ctrl;
i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
mutex_enter(&lock->cl_mutex);
VERIFY3P(lock->cl_owner, ==, txn);
VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
VERIFY3U(lock->cl_nexus_thr, ==, curthread);
if (list_is_empty(&lock->cl_stack) != 0) {
lock->cl_nexus_thr = 0;
}
mutex_exit(&lock->cl_mutex);
}
void
i2c_txn_nexus_op_begin(i2c_txn_t *txn)
{
i2c_ctrl_t *ctrl = txn->txn_ctrl;
i2c_ctrl_lock_t *lock = &ctrl->ic_lock;
mutex_enter(&lock->cl_mutex);
VERIFY3P(lock->cl_owner, ==, txn);
VERIFY3U(txn->txn_state, ==, I2C_TXN_STATE_ACQUIRED);
VERIFY0(lock->cl_nexus_thr);
if (lock->cl_nexus_thr != 0) {
VERIFY3U(lock->cl_nexus_thr, ==, curthread);
}
lock->cl_nexus_thr = (uintptr_t)curthread;
lock->cl_nnexus++;
mutex_exit(&lock->cl_mutex);
}