/* * The lowest bit in the SIOX status word signals if the in-device watchdog is * ok. If the bit is set, the device is functional. * * On writing the watchdog timer is reset when this bit toggles.
*/ #define SIOX_STATUS_WDG 0x01
/* * Bits 1 to 3 of the status word read as the bitwise negation of what was * clocked in before. The value clocked in is changed in each cycle and so * allows to detect transmit/receive problems.
*/ #define SIOX_STATUS_COUNTER 0x0e
/* * Each Siox-Device has a 4 bit type number that is neither 0 nor 15. This is * available in the upper nibble of the read status. * * On write these bits are DC.
*/ #define SIOX_STATUS_TYPE 0xf0
staticinline u8 siox_status_clean(u8 status_read, u8 status_written)
{ /* * bits 3:1 of status sample the respective bit in the status * byte written in the previous cycle but inverted. So if you wrote the * status word as 0xa before (counter = 0b101), it is expected to get * back the counter bits as 0b010. * * So given the last status written this function toggles the there * unset counter bits in the read value such that the counter bits in * the return value are all zero iff the bits were read as expected to * simplify error detection.
*/
/* * If the device knows which value the type bits should have, check * against this value otherwise just rule out the invalid values 0b0000 * and 0b1111.
*/ if (sdevice->statustype) { if (statustype != sdevice->statustype) returntrue;
} else { switch (statustype) { case 0: case 0xf: returntrue;
}
}
/* * If there is a type or counter error the device is called "unsynced".
*/ bool siox_device_synced(struct siox_device *sdevice)
{ if (siox_device_type_error(sdevice, sdevice->status_read_clean)) returnfalse;
/* * A device is called "connected" if it is synced and the watchdog is not * asserted.
*/ bool siox_device_connected(struct siox_device *sdevice)
{ if (!siox_device_synced(sdevice)) returnfalse;
staticvoid siox_poll(struct siox_master *smaster)
{ struct siox_device *sdevice;
size_t i = smaster->setbuf_len; unsignedint 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;
/* 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;
/* 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;
/* * Set the task to idle while holding the lock. This makes sure * that we don't sleep too long when the bus is reenabled before * schedule_timeout is reached.
*/ if (timeout > 0)
set_current_state(TASK_IDLE);
siox_master_unlock(smaster);
if (timeout > 0)
schedule_timeout(timeout);
/* * I'm not clear if/why it is important to set the state to * RUNNING again, but it fixes a "do not call blocking ops when * !TASK_RUNNING;"-warning.
*/
set_current_state(TASK_RUNNING);
}
}
/* * This must be done without holding the master lock because we're * called from device_remove_store which also holds a sysfs mutex. * device_unregister tries to aquire the same lock.
*/
device_unregister(&sdevice->dev);
}
int __siox_driver_register(struct siox_driver *sdriver, struct module *owner)
{ int ret;
if (unlikely(!siox_is_registered)) return -EPROBE_DEFER;
if (!sdriver->probe ||
(!sdriver->set_data && !sdriver->get_data)) {
pr_err("Driver %s doesn't provide needed callbacks\n",
sdriver->driver.name); return -EINVAL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.