+/**
+ * spi_display_xfer_agreement - Print the current target transfer agreement
+ * @starget: The target for which to display the agreement
+ *
+ * Each SPI port is required to maintain a transfer agreement for each
+ * other port on the bus. This function prints a one-line summary of
+ * the current agreement; more detailed information is available in sysfs.
+ */
+void spi_display_xfer_agreement(struct scsi_target *starget)
+{
+ struct spi_transport_attrs *tp;
+ tp = (struct spi_transport_attrs *)&starget->starget_data;
+
+ if (tp->offset > 0 && tp->period > 0) {
+ unsigned int picosec, kb100;
+ char *scsi = "FAST-?";
+ char tmp[8];
+
+ if (tp->period <= SPI_STATIC_PPR) {
+ picosec = ppr_to_ps[tp->period];
+ switch (tp->period) {
+ case 7: scsi = "FAST-320"; break;
+ case 8: scsi = "FAST-160"; break;
+ case 9: scsi = "FAST-80"; break;
+ case 10:
+ case 11: scsi = "FAST-40"; break;
+ case 12: scsi = "FAST-20"; break;
+ }
+ } else {
+ picosec = tp->period * 4000;
+ if (tp->period < 25)
+ scsi = "FAST-20";
+ else if (tp->period < 50)
+ scsi = "FAST-10";
+ else
+ scsi = "FAST-5";
+ }
+
+ kb100 = (10000000 + picosec / 2) / picosec;
+ if (tp->width)
+ kb100 *= 2;
+ sprint_frac(tmp, picosec, 1000);
+
+ dev_info(&starget->dev,
+ "%s %sSCSI %d.%d MB/s %s%s%s%s%s%s%s%s (%s ns, offset %d)\n",
+ scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10,
+ tp->dt ? "DT" : "ST",
+ tp->iu ? " IU" : "",
+ tp->qas ? " QAS" : "",
+ tp->rd_strm ? " RDSTRM" : "",
+ tp->rti ? " RTI" : "",
+ tp->wr_flow ? " WRFLOW" : "",
+ tp->pcomp_en ? " PCOMP" : "",
+ tp->hold_mcs ? " HMCS" : "",
+ tmp, tp->offset);
+ } else {
+ dev_info(&starget->dev, "%sasynchronous\n",
+ tp->width ? "wide " : "");
+ }
+}
+EXPORT_SYMBOL(spi_display_xfer_agreement);
+
+int spi_populate_width_msg(unsigned char *msg, int width)
+{
+ msg[0] = EXTENDED_MESSAGE;
+ msg[1] = 2;
+ msg[2] = EXTENDED_WDTR;
+ msg[3] = width;
+ return 4;
+}
+EXPORT_SYMBOL_GPL(spi_populate_width_msg);
+
+int spi_populate_sync_msg(unsigned char *msg, int period, int offset)
+{
+ msg[0] = EXTENDED_MESSAGE;
+ msg[1] = 3;
+ msg[2] = EXTENDED_SDTR;
+ msg[3] = period;
+ msg[4] = offset;
+ return 5;
+}
+EXPORT_SYMBOL_GPL(spi_populate_sync_msg);
+
+int spi_populate_ppr_msg(unsigned char *msg, int period, int offset,
+ int width, int options)
+{
+ msg[0] = EXTENDED_MESSAGE;
+ msg[1] = 6;
+ msg[2] = EXTENDED_PPR;
+ msg[3] = period;
+ msg[4] = 0;
+ msg[5] = offset;
+ msg[6] = width;
+ msg[7] = options;
+ return 8;
+}
+EXPORT_SYMBOL_GPL(spi_populate_ppr_msg);
+
+#ifdef CONFIG_SCSI_CONSTANTS
+static const char * const one_byte_msgs[] = {
+/* 0x00 */ "Task Complete", NULL /* Extended Message */, "Save Pointers",
+/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error",
+/* 0x06 */ "Abort Task Set", "Message Reject", "Nop", "Message Parity Error",
+/* 0x0a */ "Linked Command Complete", "Linked Command Complete w/flag",
+/* 0x0c */ "Target Reset", "Abort Task", "Clear Task Set",
+/* 0x0f */ "Initiate Recovery", "Release Recovery",
+/* 0x11 */ "Terminate Process", "Continue Task", "Target Transfer Disable",
+/* 0x14 */ NULL, NULL, "Clear ACA", "LUN Reset"
+};
+
+static const char * const two_byte_msgs[] = {
+/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag",
+/* 0x23 */ "Ignore Wide Residue", "ACA"
+};
+
+static const char * const extended_msgs[] = {
+/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
+/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request",
+/* 0x04 */ "Parallel Protocol Request", "Modify Bidirectional Data Pointer"
+};
+
+static void print_nego(const unsigned char *msg, int per, int off, int width)
+{
+ if (per) {
+ char buf[20];
+ period_to_str(buf, msg[per]);
+ printk("period = %s ns ", buf);
+ }
+
+ if (off)
+ printk("offset = %d ", msg[off]);
+ if (width)
+ printk("width = %d ", 8 << msg[width]);
+}
+
+static void print_ptr(const unsigned char *msg, int msb, const char *desc)
+{
+ int ptr = (msg[msb] << 24) | (msg[msb+1] << 16) | (msg[msb+2] << 8) |
+ msg[msb+3];
+ printk("%s = %d ", desc, ptr);
+}
+
+int spi_print_msg(const unsigned char *msg)
+{
+ int len = 1, i;
+ if (msg[0] == EXTENDED_MESSAGE) {
+ len = 2 + msg[1];
+ if (len == 2)
+ len += 256;
+ if (msg[2] < ARRAY_SIZE(extended_msgs))
+ printk ("%s ", extended_msgs[msg[2]]);
+ else
+ printk ("Extended Message, reserved code (0x%02x) ",
+ (int) msg[2]);
+ switch (msg[2]) {
+ case EXTENDED_MODIFY_DATA_POINTER:
+ print_ptr(msg, 3, "pointer");
+ break;
+ case EXTENDED_SDTR:
+ print_nego(msg, 3, 4, 0);
+ break;
+ case EXTENDED_WDTR:
+ print_nego(msg, 0, 0, 3);
+ break;
+ case EXTENDED_PPR:
+ print_nego(msg, 3, 5, 6);
+ break;
+ case EXTENDED_MODIFY_BIDI_DATA_PTR:
+ print_ptr(msg, 3, "out");
+ print_ptr(msg, 7, "in");
+ break;
+ default:
+ for (i = 2; i < len; ++i)
+ printk("%02x ", msg[i]);
+ }
+ /* Identify */
+ } else if (msg[0] & 0x80) {
+ printk("Identify disconnect %sallowed %s %d ",
+ (msg[0] & 0x40) ? "" : "not ",
+ (msg[0] & 0x20) ? "target routine" : "lun",
+ msg[0] & 0x7);
+ /* Normal One byte */
+ } else if (msg[0] < 0x1f) {
+ if (msg[0] < ARRAY_SIZE(one_byte_msgs) && one_byte_msgs[msg[0]])
+ printk("%s ", one_byte_msgs[msg[0]]);
+ else
+ printk("reserved (%02x) ", msg[0]);
+ } else if (msg[0] == 0x55) {
+ printk("QAS Request ");
+ /* Two byte */
+ } else if (msg[0] <= 0x2f) {
+ if ((msg[0] - 0x20) < ARRAY_SIZE(two_byte_msgs))
+ printk("%s %02x ", two_byte_msgs[msg[0] - 0x20],
+ msg[1]);
+ else
+ printk("reserved two byte (%02x %02x) ",
+ msg[0], msg[1]);
+ len = 2;
+ } else
+ printk("reserved ");
+ return len;
+}
+EXPORT_SYMBOL(spi_print_msg);
+
+#else /* ifndef CONFIG_SCSI_CONSTANTS */
+
+int spi_print_msg(const unsigned char *msg)
+{
+ int len = 1, i;
+
+ if (msg[0] == EXTENDED_MESSAGE) {
+ len = 2 + msg[1];
+ if (len == 2)
+ len += 256;
+ for (i = 0; i < len; ++i)
+ printk("%02x ", msg[i]);
+ /* Identify */
+ } else if (msg[0] & 0x80) {
+ printk("%02x ", msg[0]);
+ /* Normal One byte */
+ } else if ((msg[0] < 0x1f) || (msg[0] == 0x55)) {
+ printk("%02x ", msg[0]);
+ /* Two byte */
+ } else if (msg[0] <= 0x2f) {
+ printk("%02x %02x", msg[0], msg[1]);
+ len = 2;
+ } else
+ printk("%02x ", msg[0]);
+ return len;
+}
+EXPORT_SYMBOL(spi_print_msg);
+#endif /* ! CONFIG_SCSI_CONSTANTS */
+