#include <sys/types.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/open.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/file.h>
#include <sys/modctl.h>
#include <sys/i2c/misc/i2c_svc.h>
#include <sys/i2c/misc/i2c_svc_impl.h>
kmutex_t i2c_svc_mutex;
static struct modldrv i2c_modldrv = {
&mod_miscops,
"I2C module",
NULL,
};
static struct modlinkage i2c_modlinkage = {
MODREV_1,
&i2c_modldrv,
0
};
i2c_nexus_reg_list_t *nexus_reg_list_head = NULL;
int i2csvcdebug = 0;
int
_init(void)
{
int error;
if ((error = mod_install(&i2c_modlinkage)) == 0) {
mutex_init(&i2c_svc_mutex, NULL, MUTEX_DRIVER, NULL);
}
return (error);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&i2c_modlinkage)) == 0) {
mutex_destroy(&i2c_svc_mutex);
}
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&i2c_modlinkage, modinfop));
}
int
i2c_client_register(dev_info_t *dip, i2c_client_hdl_t *i2c_hdl)
{
dev_info_t *pdip;
i2c_client_hdl_t hdl;
i2c_nexus_reg_list_t *reg_list;
pdip = ddi_get_parent(dip);
mutex_enter(&i2c_svc_mutex);
reg_list = nexus_reg_list_head;
for (; reg_list != NULL; reg_list = reg_list->next) {
if (reg_list->dip == pdip) {
break;
}
}
mutex_exit(&i2c_svc_mutex);
if (reg_list == NULL) {
return (I2C_FAILURE);
}
hdl = kmem_alloc(sizeof (struct i2c_client_hdl_impl), KM_SLEEP);
CHDL(hdl)->chdl_dip = dip;
CHDL(hdl)->chdl_nexus_reg = ®_list->nexus_reg;
*i2c_hdl = hdl;
return (I2C_SUCCESS);
}
void
i2c_client_unregister(i2c_client_hdl_t hdl)
{
kmem_free(hdl, sizeof (struct i2c_client_hdl_impl));
}
int
i2c_transfer(i2c_client_hdl_t hdl, i2c_transfer_t *i2c_tran)
{
switch (i2c_tran->i2c_flags) {
case I2C_WR:
if (i2c_tran->i2c_wlen == 0) {
return (EINVAL);
}
break;
case I2C_RD:
if (i2c_tran->i2c_rlen == 0) {
return (EINVAL);
}
break;
case I2C_WR_RD:
if (i2c_tran->i2c_wlen == 0 || i2c_tran->i2c_rlen == 0) {
return (EINVAL);
}
break;
default:
return (EINVAL);
}
if (CHDL(hdl)->chdl_nexus_reg->i2c_nexus_transfer != NULL) {
(*CHDL(hdl)->chdl_nexus_reg->i2c_nexus_transfer)
(CHDL(hdl)->chdl_dip, i2c_tran);
return (i2c_tran->i2c_result);
} else {
return (ENOTSUP);
}
}
int
i2c_transfer_alloc(i2c_client_hdl_t hdl,
i2c_transfer_t **i2c,
ushort_t wlen,
ushort_t rlen,
uint_t flags)
{
i2c_transfer_alloc_t *i2cw;
int sleep;
int size;
*i2c = NULL;
if (flags & I2C_SLEEP) {
sleep = KM_SLEEP;
} else if (flags & I2C_NOSLEEP) {
sleep = KM_NOSLEEP;
} else {
sleep = KM_NOSLEEP;
}
size = sizeof (i2c_transfer_alloc_t) + rlen + wlen;
if ((i2cw = kmem_zalloc(size, sleep)) == NULL) {
return (I2C_FAILURE);
}
i2cw->i2cw_size = size;
i2cw->i2cw_i2ct.i2c_wlen = wlen;
i2cw->i2cw_i2ct.i2c_rlen = rlen;
if (wlen != 0) {
i2cw->i2cw_i2ct.i2c_wbuf = (uchar_t *)i2cw +
sizeof (i2c_transfer_alloc_t);
}
if (rlen != 0) {
i2cw->i2cw_i2ct.i2c_rbuf = (uchar_t *)i2cw +
sizeof (i2c_transfer_alloc_t) + wlen;
}
*i2c = (i2c_transfer_t *)i2cw;
return (I2C_SUCCESS);
}
void
i2c_transfer_free(i2c_client_hdl_t hdl, i2c_transfer_t *i2c_tran)
{
i2c_transfer_alloc_t *i2cw = (i2c_transfer_alloc_t *)i2c_tran;
kmem_free(i2cw, i2cw->i2cw_size);
}
void
i2c_nexus_register(dev_info_t *dip, i2c_nexus_reg_t *nexus_reg)
{
i2c_nexus_reg_list_t *nexus_reglist;
nexus_reglist = kmem_alloc(sizeof (struct i2c_nexus_reg_list),
KM_SLEEP);
mutex_enter(&i2c_svc_mutex);
nexus_reglist->next = nexus_reg_list_head;
nexus_reg_list_head = nexus_reglist;
mutex_exit(&i2c_svc_mutex);
nexus_reglist->nexus_reg = *nexus_reg;
nexus_reglist->dip = dip;
}
void
i2c_nexus_unregister(dev_info_t *dip)
{
i2c_nexus_reg_list_t **reg_list;
i2c_nexus_reg_list_t *save = NULL;
mutex_enter(&i2c_svc_mutex);
reg_list = &nexus_reg_list_head;
for (; *reg_list != NULL; reg_list = &(*reg_list)->next) {
if ((*reg_list)->dip == dip) {
save = *reg_list;
*reg_list = (*reg_list)->next;
break;
}
}
mutex_exit(&i2c_svc_mutex);
if (save != NULL) {
kmem_free(save, sizeof (i2c_nexus_reg_list_t));
} else {
cmn_err(CE_WARN, "could not find nexus reg to free");
}
}