/* 
 * $Id: zh.h,v 1.7.2.2 2004/03/24 11:18:00 aherrman Exp $
 *
 * Module providing an interface for HBA API (FC-HBA) implementation
 * to the zfcp driver.
 *
 * (C) Copyright IBM Corp. 2003
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 *
 * Authors:
 *       Stefan Voelkel <Stefan.Voelkel@millenux.com>
 *       Andreas Herrmann <aherrman@de.ibm.com>
 */

/*
 * We expect wwn to be a 64 bit type and not to be an array of 8 characters.
 * Conversion of wwn thus has to be done in vendor/os specific library.
 */

#ifndef _ZH_H_
#define _ZH_H_

#include <linux/ioctl.h>
#include <asm/string.h>

#include "zfcp_zh.h"

typedef u64 devid_t;

#define DEVID_TO_DEVNO(devid) (devno_t)(devid & 0xffff)
#define ZH_DEVID(scsid, cssid, devno) \
 ((devid_t) (scsid << 24) | (cssid << 16) | devno)

/* Maximum number of events in the queues (shared and polled) */
#define ZH_EVENTS_MAX 20
#define ZH_GET_EVENT_BUFFER_COUNT 10

/* SPC-2 defines the addional_length field in the sense data format as a byte,
 * thus only 255 bytes of additional data may be returned. Size of header in
 * sense data format is 8 bytes.
 */
#define ZH_SCSI_SENSE_BUFFERSIZE   263

typedef unsigned short zh_count_t;

/**
 * struct sg_list - ng scatterlist
 * @*sg: pointer to the real scatterlist
 * @count: number of allocated pages
 *
 * Even if this struct is small, it groups logical depended information. And it
 * reduces parameter list length.
 */
struct sg_list
{
	struct scatterlist *sg;
	unsigned int count;
};

/**
 * struct scsi_inquiry_cmd
 * FIXME: to be documented
 * The following are defined by the SCSI-2 specification.
 */
struct scsi_inquiry_cmd
{
	u8 op;
	u8 reserved1:6;
	u8 cmdt:1;
	u8 evpd:1;
	u8 page_code;
	u8 reserved2;
	u8 alloc_length;
	u8 control;
} __attribute__((packed));

/**
 * struct scsi_read_capacity_cmd
 * FIXME: to be documented
 * the 10 byte version of the command, see SBC-2
 */
struct scsi_read_capacity_cmd
{
	u8 op;
	u8 reserved1:7;
	u8 reladdr:1;
	u32 lba;
	u16 reserved2;
	u8 reserved3:7;
	u8 pmi:1;
	u8 control;
} __attribute__((packed));

/**
 * struct scsi_report_luns_cmd
 * FIXME: to be documented
 */
struct scsi_report_luns_cmd
{
	u8 op;
	u8 reserved1[5];
	u32 alloc_length;
	u8 reserved2;
	u8 control;
} __attribute__((packed));

/* minimum size of the response data for REPORT LUNS */ 
#define SCSI_REPORT_LUNS_SIZE_MIN 8

#ifndef REPORT_LUNS
#define REPORT_LUNS 0xA0
#endif

/**
 * struct zh_get_config
 * @devid: of the adapter to generate config events for
 * @wwpn: of the port to generate config events for
 * @size: must be set to sizeof(struct zh_event)
 *
 * The size member is used to assert that the events structure in user- and
 * kernelspace are of the same size.
 * If devid is 0, adapter_add events are generated for all configured adapters.
 * If devid is != 0 and wwpn is zero, port_add events are generated for all
 * ports of the specified adapter.
 * if devid != 0 and wwpn != 0, unit_add events are generated for all units
 * configured on the specified port;
 */
struct zh_get_config
{
	devid_t devid;
	wwn_t wwpn;
	unsigned int flags:2;
};

/**
 * struct zh_get_adapterattributes
 * @devid: unique id for adapter device
 * @attributes: adapter attributes
 *
 * structure passed by ioctl ZH_IOC_GET_ADAPTERATTRIBUTES
 */
struct zh_get_adapterattributes
{
	devid_t devid;
	struct zfcp_adapter_attributes attributes;
};

/**
 * struct zh_get_portattributes
 * @devid: unique id for adapter device
 * @wwpn: wwn of discovered or local port (optional)
 * @attributes: port attributes
 *
 * structure passed by ioctls ZH_IOC_GET_PORTATTRIBUTES and
 * ZH_IOC_GET_DPORTATTRIBUTES
 */
struct zh_get_portattributes
{
	devid_t devid;
	wwn_t wwpn;
	struct zfcp_port_attributes attributes;
};

/**
 * struct zh_get_portstatistics
 * @devid: unique id for adapter device
 * @portwwn: wwn local port (optional)
 * @stat: port statistics
 *
 * structure passed by ioctl ZH_IOC_GET_PORTSTATISTICS
 */
struct zh_get_portstatistics
{
	devid_t devid;
	wwn_t portwwn;
	struct zfcp_port_statistics statistics;
};

/**
 * struct zh_event_polled_link
 * @event: subtype of link event, see @zh_event_polled_link_e
 * @port_fc_id: port FC id, where this event occured
 * 
 * The standard defines an array of 3 u32 as reserved
 * in its structure. We do not need this here since no data
 * is passed with this member from kernel, to userspace
 */
struct zh_event_polled_link
{
  u32 port_fc_id;
};

/**
 * struct zh_event_polled_rscn
 * @port_fc_id: port FC id, where this event occured
 * @port_page: affected port_id pages
 * 
 * The standard defines an array of 2 u32 as reserved
 * in its structure. We do not need this here since no data
 * is passed with this member from kernel, to userspace
 */
struct zh_event_polled_rscn
{
  u32 port_fc_id;
  u32 port_page;
};

/**
 * struct zh_event_polled_pty
 * @pty_data: proprietary data
 */
struct zh_event_polled_pty
{
  u32 pty_data[4];
};

/**
 * struct zh_event_polled
 * @event: type of occured event
 * @data: union of diffrent events
 * @link: link event, @see zh_event_polled_link
 * @rscn: rscn event, @see zh_event_polled_rscn
 * @pty: pty event, @see zh_event_polled_pty
 */
struct zh_event_polled
{
  u32 event;
  devid_t devid;
  union
  {
    struct zh_event_polled_link link;
    struct zh_event_polled_rscn rscn;
    struct zh_event_polled_pty pty;
  } data;
};

/**
 * struct zh_get_event_buffer
 * @devid: of the adapter to poll events
 * @polled: array of events
 */
struct zh_get_event_buffer
{
	devid_t devid;
	unsigned int count;
	struct zh_event_polled polled[ZH_GET_EVENT_BUFFER_COUNT];
};

/**
 * struct zh_event_adapter_add
 * @devid: unique id of adapter device
 * @wwnn: wwn of adapter node
 * @wwpn: wwn of adapter port
 *
 * structure used for adapter add events
 */
struct zh_event_adapter_add
{
	devid_t	devid;
	wwn_t	wwnn;
	wwn_t	wwpn;
};

/**
 * struct zh_event_port_edd
 * @devid: of the adapter the port was added
 * @wwpn: of the added port
 * @wwnn: of the added port
 * @did: of the added port
 */
struct zh_event_port_add
{
	devid_t	devid;
	wwn_t wwpn;
	wwn_t wwnn;
	fc_id_t did;
};

/**
 * struct zh_event_unit_add
 * @devid: of the adapter the unit was added
 * @wwpn: of the adapter the unit was added
 * @fclun: of the added unit
 * @host: SCSI host id
 * @channel: SCSI channel
 * @id: SCSI id
 * @lun: SCSI lun
 */
struct zh_event_unit_add
{
	devid_t	devid;
	wwn_t wwpn;
	fcp_lun_t fclun;
	unsigned int host;
	unsigned int channel;
	unsigned int id;
	unsigned int lun;
};

/**
 * struct zh_event
 * @event: type of event
 * @data: union of event specific structures
 *
 * structure passed by ioctl ZH_IOC_EVENT (and others?)
 */
struct zh_event
{
	u8 event;
	union
	{
		struct zh_event_polled polled;
		struct zh_event_adapter_add adapter_add;
		struct zh_event_port_add port_add;
		struct zh_event_unit_add unit_add;
	} data;
};

/* SPC-2 defines the addional_length field of the inquiry reply as a byte, thus
 * only 255 bytes of additional data may be returned. Size of header for
 * standard INQUIRY data is 5 bytes.
 */
#define ZH_SCSI_INQUIRY_SIZE 260

/**
 * struct zh_scsi_inquiry - data needed for an INQUIRY
 * @devid: of the adapter
 * @wwpn: of the port
 * @fclun: of the unit to send the command to
 * @evpd: request EVPD?
 * @page_code: of the EVPD to request
 * @inquiry: payload of the response
 * @sense: buffer for sense data
 */
struct zh_scsi_inquiry
{
	devid_t devid;
	wwn_t wwpn;
	u64 fclun;
	u8 evpd;
	u32 page_code;
	u8 inquiry[ZH_SCSI_INQUIRY_SIZE];
	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
};

/* SBC-2 defines the READ CAPACITY data */
#define ZH_SCSI_READ_CAPACITY_SIZE 8

/**
 * struct zh_scsi_read_capacity - data needed for a READ_CAPACITY
 * @devid: of the adapter
 * @wwpn: of the port
 * @fclun: of the unit to send the command to
 * @read_capacity: payload of the response
 * @sense: buffer for sense data
 */
struct zh_scsi_read_capacity
{
	devid_t devid;
	wwn_t wwpn;
	u64 fclun;
	u8 read_capacity[ZH_SCSI_READ_CAPACITY_SIZE];
	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
};

/**
 * struct zh_scsi_report_luns - data needed for an REPORT_LUNS
 * @devid: of the adapter
 * @wwpn: of the port
 * @*rsp_buffer: pointer to response buffer
 * @rsp_buffer_size: of the response buffer
 * @sense: buffer for sense data
 */
struct zh_scsi_report_luns
{
	devid_t devid;
	wwn_t wwpn;
	void *rsp_buffer;
	u32 rsp_buffer_size;
	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
} __attribute__((packed));

/**
 * struct zh_els_rscn_payload - RSCN ELS payload as of FC-FS
 * @qualifier: event qualifier
 * @address_format: format of the address
 * @domain:
 * @area:
 * @sequence:
 */
struct zh_els_rscn_payload
{
	u8 reserved1:2;
	u8 qualifier:4;
	u8 address_format:2;
	u8 domain;
	u8 area;
	u8 sequence;
} __attribute__((packed));

/**
 * struct zh_els_rscn - RSCN ELS as of FC-FS
 * FIXME: to be documented
 */
struct zh_els_rscn
{
	u8 op;
	u8 page_length;
	u16 payload_length;
	struct zh_els_rscn_payload payload[0];
} __attribute__((packed));

/**
 * struct zh_get_rnid - retrieve RNID from adapter
 * @devid: to send the rnid via
 * @payload: payload for RNID ELS
 */
struct zh_get_rnid
{
	devid_t devid;
	struct zfcp_ls_rnid_acc payload;
};

/**
 * struct zh_send_rnid - send out an RNID ELS
 * @devid: to send the rnid via
 * @wwpn: to send it to
 * @size: of the buffer
 * @payload: payload buffer
 */
struct zh_send_rnid
{
	devid_t devid;
	wwn_t wwpn;
	u32 size;
	struct zfcp_ls_rnid_acc payload;
};

/**
 * struct zh_send_ct - data needed to send out a Generic Service command
 * struct zh_send_ct - send out a Generic Service command 
 * @devid: id of HBA via which to send CT
 * @req_length: size the request buffer
 * @req: request buffer
 * @resp_length: size of response buffer
 * @resp: response buffer
 */
struct zh_send_ct
{
	devid_t devid;
	u32 req_length;
	void *req;
	u32 resp_length;
	void *resp;
} __attribute__((packed));

/* IOCTL's */
#define ZH_IOC_MAGIC 0xDD

#define ZH_IOC_GET_ADAPTERATTRIBUTES \
 _IOWR(ZH_IOC_MAGIC, 1, struct zh_get_adapterattributes)
#define ZH_IOC_GET_PORTATTRIBUTES \
 _IOWR(ZH_IOC_MAGIC, 2, struct zh_get_portattributes)
#define ZH_IOC_GET_PORTSTATISTICS \
 _IOWR(ZH_IOC_MAGIC, 3, struct zh_get_portstatistics)
#define ZH_IOC_GET_DPORTATTRIBUTES \
 _IOWR(ZH_IOC_MAGIC, 4, struct zh_get_portattributes)
#define ZH_IOC_GET_RNID _IOWR(ZH_IOC_MAGIC, 5, struct zh_get_rnid)
#define ZH_IOC_SEND_RNID _IOWR(ZH_IOC_MAGIC, 6, struct zh_send_rnid)
#define ZH_IOC_SEND_CT _IOWR(ZH_IOC_MAGIC, 7, struct zh_send_ct)
#define ZH_IOC_SCSI_INQUIRY _IOW(ZH_IOC_MAGIC, 8, struct zh_scsi_inquiry)
#define ZH_IOC_SCSI_READ_CAPACITY \
 _IOW(ZH_IOC_MAGIC, 9, struct zh_scsi_read_capacity)
#define ZH_IOC_SCSI_REPORT_LUNS \
 _IOW(ZH_IOC_MAGIC, 10, struct zh_scsi_report_luns)
#define ZH_IOC_GET_EVENT_BUFFER \
 _IOWR(ZH_IOC_MAGIC, 11, struct zh_get_event_buffer)
#define ZH_IOC_GET_CONFIG _IOW(ZH_IOC_MAGIC, 12, struct zh_get_config)
#define ZH_IOC_CLEAR_CONFIG _IO(ZH_IOC_MAGIC, 13)
#define ZH_IOC_EVENT_START _IO(ZH_IOC_MAGIC, 14)
#define ZH_IOC_EVENT_STOP _IO(ZH_IOC_MAGIC, 15)
#define ZH_IOC_EVENT _IOR(ZH_IOC_MAGIC, 16, struct zh_event)
#define ZH_IOC_EVENT_INSERT _IO(ZH_IOC_MAGIC, 17)

enum zh_event_e {
	ZH_EVENT_DUMMY,
	ZH_EVENT_ADAPTER_ADD,
	ZH_EVENT_ADAPTER_DEL,
	ZH_EVENT_PORT_ADD,
	ZH_EVENT_UNIT_ADD,
	ZH_EVENT_POLLED
};

enum zh_event_polled_e {
	ZH_EVENT_POLLED_LINK_UP,
	ZH_EVENT_POLLED_LINK_DOWN,
	ZH_EVENT_POLLED_RSCN,
	ZH_EVENT_POLLED_PTY
};

#define ZH_LOG(level, fmt, args...) \
printk(level"%s:%d, "fmt, __FUNCTION__, __LINE__, ##args)

int zh_send_ct_helper(struct zh_send_ct *);
int zh_report_luns_helper(struct zh_scsi_report_luns *);

#endif /* _ZH_H_ */
