Linux-libre 4.9.189-gnu
[librecmc/linux-libre.git] / drivers / staging / unisys / include / iochannel.h
1 /* Copyright (C) 2010 - 2013 UNISYS CORPORATION */
2 /* All rights reserved. */
3 #ifndef __IOCHANNEL_H__
4 #define __IOCHANNEL_H__
5
6 /*
7  * Everything needed for IOPart-GuestPart communication is define in
8  * this file.  Note: Everything is OS-independent because this file is
9  * used by Windows, Linux and possible EFI drivers.
10  */
11
12 /*
13  * Communication flow between the IOPart and GuestPart uses the channel headers
14  * channel state.  The following states are currently being used:
15  *       UNINIT(All Zeroes), CHANNEL_ATTACHING, CHANNEL_ATTACHED, CHANNEL_OPENED
16  *
17  * additional states will be used later.  No locking is needed to switch between
18  * states due to the following rules:
19  *
20  *      1.  IOPart is only the only partition allowed to change from UNIT
21  *      2.  IOPart is only the only partition allowed to change from
22  *              CHANNEL_ATTACHING
23  *      3.  GuestPart is only the only partition allowed to change from
24  *              CHANNEL_ATTACHED
25  *
26  * The state changes are the following: IOPart sees the channel is in UNINIT,
27  *        UNINIT -> CHANNEL_ATTACHING (performed only by IOPart)
28  *        CHANNEL_ATTACHING -> CHANNEL_ATTACHED (performed only by IOPart)
29  *        CHANNEL_ATTACHED -> CHANNEL_OPENED (performed only by GuestPart)
30  */
31
32 #include <linux/uuid.h>
33
34 #include <linux/dma-direction.h>
35 #include "channel.h"
36
37 #define ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
38 #define ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
39 #define ULTRA_VSWITCH_CHANNEL_PROTOCOL_SIGNATURE \
40         ULTRA_CHANNEL_PROTOCOL_SIGNATURE
41
42 /* Must increment these whenever you insert or delete fields within this channel
43  * struct.  Also increment whenever you change the meaning of fields within this
44  * channel struct so as to break pre-existing software.  Note that you can
45  * usually add fields to the END of the channel struct withOUT needing to
46  * increment this.
47  */
48 #define ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID 2
49 #define ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID 2
50 #define ULTRA_VSWITCH_CHANNEL_PROTOCOL_VERSIONID 1
51
52 #define SPAR_VHBA_CHANNEL_OK_CLIENT(ch)                 \
53         (spar_check_channel_client(ch, spar_vhba_channel_protocol_uuid, \
54                                    "vhba", MIN_IO_CHANNEL_SIZE, \
55                                    ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID, \
56                                    ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE))
57
58 #define SPAR_VNIC_CHANNEL_OK_CLIENT(ch)                 \
59         (spar_check_channel_client(ch, spar_vnic_channel_protocol_uuid, \
60                                    "vnic", MIN_IO_CHANNEL_SIZE, \
61                                    ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID, \
62                                    ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE))
63
64 /*
65  * Everything necessary to handle SCSI & NIC traffic between Guest Partition and
66  * IO Partition is defined below.
67  */
68
69 /* Defines and enums. */
70 #define MINNUM(a, b) (((a) < (b)) ? (a) : (b))
71 #define MAXNUM(a, b) (((a) > (b)) ? (a) : (b))
72
73 /* define the two queues per data channel between iopart and ioguestparts */
74 /* used by ioguestpart to 'insert' signals to iopart */
75 #define IOCHAN_TO_IOPART 0
76 /* used by ioguestpart to 'remove' signals from iopart, same previous queue */
77 #define IOCHAN_FROM_IOPART 1
78
79 /* size of cdb - i.e., scsi cmnd */
80 #define MAX_CMND_SIZE 16
81
82 #define MAX_SENSE_SIZE 64
83
84 #define MAX_PHYS_INFO 64
85
86 /* various types of network packets that can be sent in cmdrsp */
87 enum net_types {
88         NET_RCV_POST = 0,       /* submit buffer to hold receiving
89                                  * incoming packet
90                                  */
91         /* virtnic -> uisnic */
92         NET_RCV,                /* incoming packet received */
93         /* uisnic -> virtpci */
94         NET_XMIT,               /* for outgoing net packets */
95         /* virtnic -> uisnic */
96         NET_XMIT_DONE,          /* outgoing packet xmitted */
97         /* uisnic -> virtpci */
98         NET_RCV_ENBDIS,         /* enable/disable packet reception */
99         /* virtnic -> uisnic */
100         NET_RCV_ENBDIS_ACK,     /* acknowledge enable/disable packet */
101                                 /* reception */
102         /* uisnic -> virtnic */
103         NET_RCV_PROMISC,        /* enable/disable promiscuous mode */
104         /* virtnic -> uisnic */
105         NET_CONNECT_STATUS,     /* indicate the loss or restoration of a network
106                                  * connection
107                                  */
108         /* uisnic -> virtnic */
109         NET_MACADDR,            /* indicates the client has requested to update
110                                  * its MAC addr
111                                  */
112         NET_MACADDR_ACK,        /* MAC address */
113
114 };
115
116 #define         ETH_HEADER_SIZE 14      /* size of ethernet header */
117
118 #define         ETH_MIN_DATA_SIZE 46    /* minimum eth data size */
119 #define         ETH_MIN_PACKET_SIZE (ETH_HEADER_SIZE + ETH_MIN_DATA_SIZE)
120
121 #define         ETH_MAX_MTU 16384       /* maximum data size */
122
123 #ifndef MAX_MACADDR_LEN
124 #define MAX_MACADDR_LEN 6       /* number of bytes in MAC address */
125 #endif                          /* MAX_MACADDR_LEN */
126
127 /* various types of scsi task mgmt commands  */
128 enum task_mgmt_types {
129         TASK_MGMT_ABORT_TASK = 1,
130         TASK_MGMT_BUS_RESET,
131         TASK_MGMT_LUN_RESET,
132         TASK_MGMT_TARGET_RESET,
133 };
134
135 /* various types of vdisk mgmt commands  */
136 enum vdisk_mgmt_types {
137         VDISK_MGMT_ACQUIRE = 1,
138         VDISK_MGMT_RELEASE,
139 };
140
141 struct phys_info {
142         u64 pi_pfn;
143         u16 pi_off;
144         u16 pi_len;
145 } __packed;
146
147 #define MIN_NUMSIGNALS 64
148
149 /* structs with pragma pack  */
150
151 struct guest_phys_info {
152         u64 address;
153         u64 length;
154 } __packed;
155
156 #define GPI_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(struct guest_phys_info))
157
158 struct uisscsi_dest {
159         u32 channel;            /* channel == bus number */
160         u32 id;                 /* id == target number */
161         u32 lun;                /* lun == logical unit number */
162 } __packed;
163
164 struct vhba_wwnn {
165         u32 wwnn1;
166         u32 wwnn2;
167 } __packed;
168
169 /* WARNING: Values stired in this structure must contain maximum counts (not
170  * maximum values).
171  */
172 struct vhba_config_max {/* 20 bytes */
173         u32 max_channel;/* maximum channel for devices attached to this bus */
174         u32 max_id;     /* maximum SCSI ID for devices attached to bus */
175         u32 max_lun;    /* maximum SCSI LUN for devices attached to bus */
176         u32 cmd_per_lun;/* maximum number of outstanding commands per LUN */
177         u32 max_io_size;/* maximum io size for devices attached to this bus */
178         /* max io size is often determined by the resource of the hba. e.g */
179         /* max scatter gather list length * page size / sector size */
180 } __packed;
181
182 struct uiscmdrsp_scsi {
183         u64 handle;             /* the handle to the cmd that was received */
184                                 /* send it back as is in the rsp packet.  */
185         u8 cmnd[MAX_CMND_SIZE]; /* the cdb for the command */
186         u32 bufflen;            /* length of data to be transferred out or in */
187         u16 guest_phys_entries; /* Number of entries in scatter-gather list */
188         struct guest_phys_info gpi_list[MAX_PHYS_INFO]; /* physical address
189                                                          * information for each
190                                                          * fragment
191                                                          */
192         enum dma_data_direction  data_dir; /* direction of the data, if any */
193         struct uisscsi_dest vdest;      /* identifies the virtual hba, id, */
194                                         /* channel, lun to which cmd was sent */
195
196         /* Needed to queue the rsp back to cmd originator */
197         int linuxstat;          /* original Linux status used by linux vdisk */
198         u8 scsistat;            /* the scsi status */
199         u8 addlstat;            /* non-scsi status */
200 #define ADDL_SEL_TIMEOUT        4
201
202         /* the following fields are need to determine the result of command */
203          u8 sensebuf[MAX_SENSE_SIZE];   /* sense info in case cmd failed; */
204         /* it holds the sense_data struct; */
205         /* see that struct for details. */
206         void *vdisk; /* pointer to the vdisk to clean up when IO completes. */
207         int no_disk_result;
208         /* used to return no disk inquiry result
209          * when no_disk_result is set to 1,
210          * scsi.scsistat is SAM_STAT_GOOD
211          * scsi.addlstat is 0
212          * scsi.linuxstat is SAM_STAT_GOOD
213          * That is, there is NO error.
214          */
215 } __packed;
216
217 /* Defines to support sending correct inquiry result when no disk is
218  * configured.
219  */
220
221 /* From SCSI SPC2 -
222  *
223  * If the target is not capable of supporting a device on this logical unit, the
224  * device server shall set this field to 7Fh (PERIPHERAL QUALIFIER set to 011b
225  * and PERIPHERAL DEVICE TYPE set to 1Fh).
226  *
227  *The device server is capable of supporting the specified peripheral device
228  *type on this logical unit. However, the physical device is not currently
229  *connected to this logical unit.
230  */
231
232 #define DEV_NOT_CAPABLE 0x7f    /* peripheral qualifier of 0x3  */
233                                 /* peripheral type of 0x1f */
234                                 /* specifies no device but target present */
235
236 #define DEV_DISK_CAPABLE_NOT_PRESENT 0x20 /* peripheral qualifier of 0x1 */
237     /* peripheral type of 0 - disk */
238     /* specifies device capable, but not present */
239
240 #define DEV_HISUPPORT 0x10      /* HiSup = 1; shows support for report luns */
241                                 /* must be returned for lun 0. */
242
243 /* NOTE: Linux code assumes inquiry contains 36 bytes. Without checking length
244  * in buf[4] some linux code accesses bytes beyond 5 to retrieve vendor, product
245  * & revision.  Yikes! So let us always send back 36 bytes, the minimum for
246  * inquiry result.
247  */
248 #define NO_DISK_INQUIRY_RESULT_LEN 36
249
250 #define MIN_INQUIRY_RESULT_LEN 5 /* 5 bytes minimum for inquiry result */
251
252 /* SCSI device version for no disk inquiry result */
253 #define SCSI_SPC2_VER 4         /* indicates SCSI SPC2 (SPC3 is 5) */
254
255 /* Struct & Defines to support sense information. */
256
257 /* The following struct is returned in sensebuf field in uiscmdrsp_scsi.  It is
258  * initialized in exactly the manner that is recommended in Windows (hence the
259  * odd values).
260  * When set, these fields will have the following values:
261  * ErrorCode = 0x70             indicates current error
262  * Valid = 1                    indicates sense info is valid
263  * SenseKey                     contains sense key as defined by SCSI specs.
264  * AdditionalSenseCode          contains sense key as defined by SCSI specs.
265  * AdditionalSenseCodeQualifier contains qualifier to sense code as defined by
266  *                              scsi docs.
267  * AdditionalSenseLength        contains will be sizeof(sense_data)-8=10.
268  */
269 struct sense_data {
270         u8 errorcode:7;
271         u8 valid:1;
272         u8 segment_number;
273         u8 sense_key:4;
274         u8 reserved:1;
275         u8 incorrect_length:1;
276         u8 end_of_media:1;
277         u8 file_mark:1;
278         u8 information[4];
279         u8 additional_sense_length;
280         u8 command_specific_information[4];
281         u8 additional_sense_code;
282         u8 additional_sense_code_qualifier;
283         u8 fru_code;
284         u8 sense_key_specific[3];
285 } __packed;
286
287 struct net_pkt_xmt {
288         int len;        /* full length of data in the packet */
289         int num_frags;  /* number of fragments in frags containing data */
290         struct phys_info frags[MAX_PHYS_INFO];  /* physical page information */
291         char ethhdr[ETH_HEADER_SIZE];   /* the ethernet header  */
292         struct {
293                 /* these are needed for csum at uisnic end */
294                 u8 valid;       /* 1 = struct is valid - else ignore */
295                 u8 hrawoffv;    /* 1 = hwrafoff is valid */
296                 u8 nhrawoffv;   /* 1 = nhwrafoff is valid */
297                 u16 protocol;   /* specifies packet protocol */
298                 u32 csum;       /* value used to set skb->csum at IOPart */
299                 u32 hrawoff;    /* value used to set skb->h.raw at IOPart */
300                 /* hrawoff points to the start of the TRANSPORT LAYER HEADER */
301                 u32 nhrawoff;   /* value used to set skb->nh.raw at IOPart */
302                 /* nhrawoff points to the start of the NETWORK LAYER HEADER */
303         } lincsum;
304
305             /* **** NOTE ****
306              * The full packet is described in frags but the ethernet header is
307              * separately kept in ethhdr so that uisnic doesn't have "MAP" the
308              * guest memory to get to the header. uisnic needs ethhdr to
309              * determine how to route the packet.
310              */
311 } __packed;
312
313 struct net_pkt_xmtdone {
314         u32 xmt_done_result;    /* result of NET_XMIT */
315 } __packed;
316
317 /* RCVPOST_BUF_SIZe must be at most page_size(4096) - cache_line_size (64) The
318  * reason is because dev_skb_alloc which is used to generate RCV_POST skbs in
319  * virtnic requires that there is "overhead" in the buffer, and pads 16 bytes. I
320  * prefer to use 1 full cache line size for "overhead" so that transfers are
321  * better.  IOVM requires that a buffer be represented by 1 phys_info structure
322  * which can only cover page_size.
323  */
324 #define RCVPOST_BUF_SIZE 4032
325 #define MAX_NET_RCV_CHAIN \
326         ((ETH_MAX_MTU + ETH_HEADER_SIZE + RCVPOST_BUF_SIZE - 1) \
327         / RCVPOST_BUF_SIZE)
328
329 struct net_pkt_rcvpost {
330             /* rcv buf size must be large enough to include ethernet data len +
331              * ethernet header len - we are choosing 2K because it is guaranteed
332              * to be describable
333              */
334             struct phys_info frag;      /* physical page information for the */
335                                         /* single fragment 2K rcv buf */
336             u64 unique_num;
337             /* unique_num ensure that receive posts are returned to */
338             /* the Adapter which we sent them originally. */
339 } __packed;
340
341 struct net_pkt_rcv {
342         /* the number of receive buffers that can be chained  */
343         /* is based on max mtu and size of each rcv buf */
344         u32 rcv_done_len;       /* length of received data */
345         u8 numrcvbufs;          /* number of receive buffers that contain the */
346         /* incoming data; guest end MUST chain these together. */
347         void *rcvbuf[MAX_NET_RCV_CHAIN];        /* list of chained rcvbufs */
348         /* each entry is a receive buffer provided by NET_RCV_POST. */
349         /* NOTE: first rcvbuf in the chain will also be provided in net.buf. */
350         u64 unique_num;
351         u32 rcvs_dropped_delta;
352 } __packed;
353
354 struct net_pkt_enbdis {
355         void *context;
356         u16 enable;             /* 1 = enable, 0 = disable */
357 } __packed;
358
359 struct net_pkt_macaddr {
360         void *context;
361         u8 macaddr[MAX_MACADDR_LEN];    /* 6 bytes */
362 } __packed;
363
364 /* cmd rsp packet used for VNIC network traffic  */
365 struct uiscmdrsp_net {
366         enum net_types type;
367         void *buf;
368         union {
369                 struct net_pkt_xmt xmt;         /* used for NET_XMIT */
370                 struct net_pkt_xmtdone xmtdone; /* used for NET_XMIT_DONE */
371                 struct net_pkt_rcvpost rcvpost; /* used for NET_RCV_POST */
372                 struct net_pkt_rcv rcv;         /* used for NET_RCV */
373                 struct net_pkt_enbdis enbdis;   /* used for NET_RCV_ENBDIS, */
374                                                 /* NET_RCV_ENBDIS_ACK,  */
375                                                 /* NET_RCV_PROMSIC, */
376                                                 /* and NET_CONNECT_STATUS */
377                 struct net_pkt_macaddr macaddr;
378         };
379 } __packed;
380
381 struct uiscmdrsp_scsitaskmgmt {
382         enum task_mgmt_types tasktype;
383
384             /* the type of task */
385         struct uisscsi_dest vdest;
386
387             /* the vdisk for which this task mgmt is generated */
388         u64 handle;
389
390             /* This is a handle that the guest has saved off for its own use.
391              * Its value is preserved by iopart & returned as is in the task
392              * mgmt rsp.
393              */
394         u64 notify_handle;
395
396            /* For linux guests, this is a pointer to wait_queue_head that a
397             * thread is waiting on to see if the taskmgmt command has completed.
398             * When the rsp is received by guest, the thread receiving the
399             * response uses this to notify the thread waiting for taskmgmt
400             * command completion.  Its value is preserved by iopart & returned
401             * as is in the task mgmt rsp.
402             */
403         u64 notifyresult_handle;
404
405             /* this is a handle to location in guest where the result of the
406              * taskmgmt command (result field) is to saved off when the response
407              * is handled.  Its value is preserved by iopart & returned as is in
408              * the task mgmt rsp.
409              */
410         char result;
411
412             /* result of taskmgmt command - set by IOPart - values are: */
413 #define TASK_MGMT_FAILED  0
414 } __packed;
415
416 /* Used by uissd to send disk add/remove notifications to Guest */
417 /* Note that the vHba pointer is not used by the Client/Guest side. */
418 struct uiscmdrsp_disknotify {
419         u8 add;                 /* 0-remove, 1-add */
420         void *v_hba;            /* channel info to route msg */
421         u32 channel, id, lun;   /* SCSI Path of Disk to added or removed */
422 } __packed;
423
424 /* The following is used by virthba/vSCSI to send the Acquire/Release commands
425  * to the IOVM.
426  */
427 struct uiscmdrsp_vdiskmgmt {
428         enum vdisk_mgmt_types vdisktype;
429
430             /* the type of task */
431         struct uisscsi_dest vdest;
432
433             /* the vdisk for which this task mgmt is generated */
434         u64 handle;
435
436             /* This is a handle that the guest has saved off for its own use.
437              * Its value is preserved by iopart & returned as is in the task
438              * mgmt rsp.
439              */
440         u64 notify_handle;
441
442             /* For linux guests, this is a pointer to wait_queue_head that a
443              * thread is waiting on to see if the tskmgmt command has completed.
444              * When the rsp is received by guest, the thread receiving the
445              * response uses this to notify the thread waiting for taskmgmt
446              * command completion.  Its value is preserved by iopart & returned
447              * as is in the task mgmt rsp.
448              */
449         u64 notifyresult_handle;
450
451             /* this is a handle to location in guest where the result of the
452              * taskmgmt command (result field) is to saved off when the response
453              * is handled.  Its value is preserved by iopart & returned as is in
454              * the task mgmt rsp.
455              */
456         char result;
457
458             /* result of taskmgmt command - set by IOPart - values are: */
459 #define VDISK_MGMT_FAILED  0
460 } __packed;
461
462 /* keeping cmd & rsp info in one structure for now cmd rsp packet for scsi */
463 struct uiscmdrsp {
464         char cmdtype;
465
466 /* describes what type of information is in the struct */
467 #define CMD_SCSI_TYPE           1
468 #define CMD_NET_TYPE            2
469 #define CMD_SCSITASKMGMT_TYPE   3
470 #define CMD_NOTIFYGUEST_TYPE    4
471 #define CMD_VDISKMGMT_TYPE      5
472         union {
473                 struct uiscmdrsp_scsi scsi;
474                 struct uiscmdrsp_net net;
475                 struct uiscmdrsp_scsitaskmgmt scsitaskmgmt;
476                 struct uiscmdrsp_disknotify disknotify;
477                 struct uiscmdrsp_vdiskmgmt vdiskmgmt;
478         };
479         void *private_data;     /* send the response when the cmd is */
480                                 /* done (scsi & scsittaskmgmt). */
481         struct uiscmdrsp *next; /* General Purpose Queue Link */
482         struct uiscmdrsp *activeQ_next; /* Used to track active commands */
483         struct uiscmdrsp *activeQ_prev; /* Used to track active commands */
484 } __packed;
485
486 struct iochannel_vhba {
487         struct vhba_wwnn wwnn;          /* 8 bytes */
488         struct vhba_config_max max;     /* 20 bytes */
489 } __packed;                             /* total = 28 bytes */
490 struct iochannel_vnic {
491         u8 macaddr[6];                  /* 6 bytes */
492         u32 num_rcv_bufs;               /* 4 bytes */
493         u32 mtu;                        /* 4 bytes */
494         uuid_le zone_uuid;              /* 16 bytes */
495 } __packed;
496 /* This is just the header of the IO channel.  It is assumed that directly after
497  * this header there is a large region of memory which contains the command and
498  * response queues as specified in cmd_q and rsp_q SIGNAL_QUEUE_HEADERS.
499  */
500 struct spar_io_channel_protocol {
501         struct channel_header channel_header;
502         struct signal_queue_header cmd_q;
503         struct signal_queue_header rsp_q;
504         union {
505                 struct iochannel_vhba vhba;
506                 struct iochannel_vnic vnic;
507         } __packed;
508
509 #define MAX_CLIENTSTRING_LEN 1024
510         /* client_string is NULL termimated so holds max -1 bytes */
511          u8 client_string[MAX_CLIENTSTRING_LEN];
512 } __packed;
513
514 /* INLINE functions for initializing and accessing I/O data channels */
515 #define SIZEOF_PROTOCOL (COVER(sizeof(struct spar_io_channel_protocol), 64))
516 #define SIZEOF_CMDRSP (COVER(sizeof(struct uiscmdrsp), 64))
517
518 #define MIN_IO_CHANNEL_SIZE COVER(SIZEOF_PROTOCOL + \
519                                   2 * MIN_NUMSIGNALS * SIZEOF_CMDRSP, 4096)
520
521 /*
522  * INLINE function for expanding a guest's pfn-off-size into multiple 4K page
523  * pfn-off-size entires.
524  */
525
526 /* use 4K page sizes when we it comes to passing page information between */
527 /* Guest and IOPartition. */
528 #define PI_PAGE_SIZE  0x1000
529 #define PI_PAGE_MASK  0x0FFF
530
531 /* returns next non-zero index on success or zero on failure (i.e. out of
532  * room)
533  */
534 static inline  u16
535 add_physinfo_entries(u64 inp_pfn, u16 inp_off, u32 inp_len, u16 index,
536                      u16 max_pi_arr_entries, struct phys_info pi_arr[])
537 {
538         u32 len;
539         u16 i, firstlen;
540
541         firstlen = PI_PAGE_SIZE - inp_off;
542         if (inp_len <= firstlen) {
543                 /* the input entry spans only one page - add as is */
544                 if (index >= max_pi_arr_entries)
545                         return 0;
546                 pi_arr[index].pi_pfn = inp_pfn;
547                 pi_arr[index].pi_off = (u16)inp_off;
548                 pi_arr[index].pi_len = (u16)inp_len;
549                 return index + 1;
550         }
551
552         /* this entry spans multiple pages */
553         for (len = inp_len, i = 0; len;
554                 len -= pi_arr[index + i].pi_len, i++) {
555                 if (index + i >= max_pi_arr_entries)
556                         return 0;
557                 pi_arr[index + i].pi_pfn = inp_pfn + i;
558                 if (i == 0) {
559                         pi_arr[index].pi_off = inp_off;
560                         pi_arr[index].pi_len = firstlen;
561                 } else {
562                         pi_arr[index + i].pi_off = 0;
563                         pi_arr[index + i].pi_len =
564                             (u16)MINNUM(len, (u32)PI_PAGE_SIZE);
565                 }
566         }
567         return index + i;
568 }
569
570 #endif                          /* __IOCHANNEL_H__ */