Date: Thu, 21 Nov 2019 19:13:05 +0000 (UTC) From: Ian Lepore <ian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r354973 - head/sys/dev/iicbus Message-ID: <201911211913.xALJD52E030031@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ian Date: Thu Nov 21 19:13:05 2019 New Revision: 354973 URL: https://svnweb.freebsd.org/changeset/base/354973 Log: Rewrite iicdev_writeto() to use a single buffer and a single iic_msg, rather than effectively doing scatter/gather IO with a pair of iic_msgs that direct the controller to do a single transfer with no bus STOP/START between the two buffers. It turns out we have multiple i2c hardware drivers that don't honor the NOSTOP and NOSTART flags; sometimes they just try to do the transfers anyway, creating confusing failures or leading to corrupted data. Modified: head/sys/dev/iicbus/iiconf.c Modified: head/sys/dev/iicbus/iiconf.c ============================================================================== --- head/sys/dev/iicbus/iiconf.c Thu Nov 21 18:49:54 2019 (r354972) +++ head/sys/dev/iicbus/iiconf.c Thu Nov 21 19:13:05 2019 (r354973) @@ -540,25 +540,47 @@ iicdev_readfrom(device_t slavedev, uint8_t regaddr, vo int iicdev_writeto(device_t slavedev, uint8_t regaddr, void *buffer, uint16_t buflen, int waithow) { - struct iic_msg msgs[2]; - uint8_t slaveaddr; + struct iic_msg msg; + uint8_t local_buffer[32]; + uint8_t *bufptr; + size_t bufsize; + int error; /* - * Two transfers back to back with no stop or start between them; first - * we write the address then we write the data to that address, all in a - * single transfer from two scattered buffers. + * Ideally, we would do two transfers back to back with no stop or start + * between them using an array of 2 iic_msgs; first we'd write the + * address byte using the IIC_M_NOSTOP flag, then we write the data + * using IIC_M_NOSTART, all in a single transfer. Unfortunately, + * several i2c hardware drivers don't support that (perhaps because the + * hardware itself can't support it). So instead we gather the + * scattered bytes into a single buffer here before writing them using a + * single iic_msg. This function is typically used to write a few bytes + * at a time, so we try to use a small local buffer on the stack, but + * fall back to allocating a temporary buffer when necessary. */ - slaveaddr = iicbus_get_addr(slavedev); - msgs[0].slave = slaveaddr; - msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP; - msgs[0].len = 1; - msgs[0].buf = ®addr; + bufsize = buflen + 1; + if (bufsize <= sizeof(local_buffer)) { + bufptr = local_buffer; + } else { + bufptr = malloc(bufsize, M_DEVBUF, + (waithow & IIC_WAIT) ? M_WAITOK : M_NOWAIT); + if (bufptr == NULL) + return (errno2iic(ENOMEM)); + } - msgs[1].slave = slaveaddr; - msgs[1].flags = IIC_M_WR | IIC_M_NOSTART; - msgs[1].len = buflen; - msgs[1].buf = buffer; + bufptr[0] = regaddr; + memcpy(&bufptr[1], buffer, buflen); - return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow)); + msg.slave = iicbus_get_addr(slavedev); + msg.flags = IIC_M_WR; + msg.len = bufsize; + msg.buf = bufptr; + + error = iicbus_transfer_excl(slavedev, &msg, 1, waithow); + + if (bufptr != local_buffer) + free(bufptr, M_DEVBUF); + + return (error); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201911211913.xALJD52E030031>