in siox-core.c [128:276]
static void siox_poll(struct siox_master *smaster)
{
struct siox_device *sdevice;
size_t i = smaster->setbuf_len;
unsigned int devno = 0;
int unsync_error = 0;
smaster->last_poll = jiffies;
/*
* The counter bits change in each second cycle, the watchdog bit
* toggles each time.
* The counter bits hold values from [0, 6]. 7 would be possible
* theoretically but the protocol designer considered that a bad idea
* for reasons unknown today. (Maybe that's because then the status read
* back has only zeros in the counter bits then which might be confused
* with a stuck-at-0 error. But for the same reason (with s/0/1/) 0
* could be skipped.)
*/
if (++smaster->status > 0x0d)
smaster->status = 0;
memset(smaster->buf, 0, smaster->setbuf_len);
/* prepare data pushed out to devices in buf[0..setbuf_len) */
list_for_each_entry(sdevice, &smaster->devices, node) {
struct siox_driver *sdriver =
to_siox_driver(sdevice->dev.driver);
sdevice->status_written = smaster->status;
i -= sdevice->inbytes;
/*
* If the device or a previous one is unsynced, don't pet the
* watchdog. This is done to ensure that the device is kept in
* reset when something is wrong.
*/
if (!siox_device_synced(sdevice))
unsync_error = 1;
if (sdriver && !unsync_error)
sdriver->set_data(sdevice, sdevice->status_written,
&smaster->buf[i + 1]);
else
/*
* Don't trigger watchdog if there is no driver or a
* sync problem
*/
sdevice->status_written &= ~SIOX_STATUS_WDG;
smaster->buf[i] = sdevice->status_written;
trace_siox_set_data(smaster, sdevice, devno, i);
devno++;
}
smaster->pushpull(smaster, smaster->setbuf_len, smaster->buf,
smaster->getbuf_len,
smaster->buf + smaster->setbuf_len);
unsync_error = 0;
/* interpret data pulled in from devices in buf[setbuf_len..] */
devno = 0;
i = smaster->setbuf_len;
list_for_each_entry(sdevice, &smaster->devices, node) {
struct siox_driver *sdriver =
to_siox_driver(sdevice->dev.driver);
u8 status = smaster->buf[i + sdevice->outbytes - 1];
u8 status_clean;
u8 prev_status_clean = sdevice->status_read_clean;
bool synced = true;
bool connected = true;
if (!siox_device_synced(sdevice))
unsync_error = 1;
/*
* If the watchdog bit wasn't toggled in this cycle, report the
* watchdog as active to give a consistent view for drivers and
* sysfs consumers.
*/
if (!sdriver || unsync_error)
status &= ~SIOX_STATUS_WDG;
status_clean =
siox_status_clean(status,
sdevice->status_written_lastcycle);
/* Check counter and type bits */
if (siox_device_counter_error(sdevice, status_clean) ||
siox_device_type_error(sdevice, status_clean)) {
bool prev_error;
synced = false;
/* only report a new error if the last cycle was ok */
prev_error =
siox_device_counter_error(sdevice,
prev_status_clean) ||
siox_device_type_error(sdevice,
prev_status_clean);
if (!prev_error) {
sdevice->status_errors++;
sysfs_notify_dirent(sdevice->status_errors_kn);
}
}
/* If the device is unsynced report the watchdog as active */
if (!synced) {
status &= ~SIOX_STATUS_WDG;
status_clean &= ~SIOX_STATUS_WDG;
}
if (siox_device_wdg_error(sdevice, status_clean))
connected = false;
/* The watchdog state changed just now */
if ((status_clean ^ prev_status_clean) & SIOX_STATUS_WDG) {
sysfs_notify_dirent(sdevice->watchdog_kn);
if (siox_device_wdg_error(sdevice, status_clean)) {
struct kernfs_node *wd_errs =
sdevice->watchdog_errors_kn;
sdevice->watchdog_errors++;
sysfs_notify_dirent(wd_errs);
}
}
if (connected != sdevice->connected)
sysfs_notify_dirent(sdevice->connected_kn);
sdevice->status_read_clean = status_clean;
sdevice->status_written_lastcycle = sdevice->status_written;
sdevice->connected = connected;
trace_siox_get_data(smaster, sdevice, devno, status_clean, i);
/* only give data read to driver if the device is connected */
if (sdriver && connected)
sdriver->get_data(sdevice, &smaster->buf[i]);
devno++;
i += sdevice->outbytes;
}
}