16969690aSAxel Dörfler/*
24535495dSIngo Weinhold * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
36969690aSAxel Dörfler * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
46969690aSAxel Dörfler * Distributed under the terms of the MIT License.
56969690aSAxel Dörfler */
6aa4ba93eSIngo Weinhold#ifndef IO_REQUEST_H
7aa4ba93eSIngo Weinhold#define IO_REQUEST_H
86969690aSAxel Dörfler
9435c43f5SIngo Weinhold
106969690aSAxel Dörfler#include <sys/uio.h>
116969690aSAxel Dörfler
1241417412SIngo Weinhold#include <new>
1341417412SIngo Weinhold
14435c43f5SIngo Weinhold#include <fs_interface.h>
156969690aSAxel Dörfler
16902559ceSIngo Weinhold#include <condition_variable.h>
17902559ceSIngo Weinhold#include <lock.h>
186969690aSAxel Dörfler#include <util/DoublyLinkedList.h>
196969690aSAxel Dörfler
206969690aSAxel Dörfler#include "dma_resources.h"
216969690aSAxel Dörfler
226969690aSAxel Dörfler
2345a206a7SAxel Dörfler#define B_PHYSICAL_IO_REQUEST	0x01	/* buffer points to physical memory */
24c1cec366SIngo Weinhold#define B_VIP_IO_REQUEST		0x02	/* used by the page writer -- make sure
25c1cec366SIngo Weinhold										   allocations won't fail */
2680048d7dSIngo Weinhold#define B_DELETE_IO_REQUEST		0x04	/* delete request when finished */
276969690aSAxel Dörfler
2830c9d3c0SAugustin Cavalierclass DMABuffer;
296969690aSAxel Dörflerstruct IOOperation;
306969690aSAxel Dörfler
31902559ceSIngo Weinholdtypedef struct IOOperation io_operation;
326969690aSAxel Dörfler
336969690aSAxel Dörflerclass IOBuffer : public DoublyLinkedListLinkImpl<IOBuffer> {
346969690aSAxel Dörflerpublic:
3541417412SIngo Weinhold	static	IOBuffer*			Create(uint32 count, bool vip);
367cc9a52dSIngo Weinhold			void				Delete();
376969690aSAxel Dörfler
386969690aSAxel Dörfler			bool				IsVirtual() const { return !fPhysical; }
396969690aSAxel Dörfler			bool				IsPhysical() const { return fPhysical; }
409951d585SIngo Weinhold			bool				IsUser() const { return fUser; }
416969690aSAxel Dörfler
42435c43f5SIngo Weinhold			void				SetVecs(generic_size_t firstVecOffset,
43435c43f5SIngo Weinhold									const generic_io_vec* vecs, uint32 count,
44435c43f5SIngo Weinhold									generic_size_t length, uint32 flags);
4545a206a7SAxel Dörfler
466969690aSAxel Dörfler			void				SetPhysical(bool physical)
476969690aSAxel Dörfler									{ fPhysical = physical; }
486969690aSAxel Dörfler			void				SetUser(bool user) { fUser = user; }
49435c43f5SIngo Weinhold			void				SetLength(generic_size_t length)
50435c43f5SIngo Weinhold									{ fLength = length; }
5145a206a7SAxel Dörfler			void				SetVecCount(uint32 count) { fVecCount = count; }
526969690aSAxel Dörfler
53435c43f5SIngo Weinhold			generic_size_t		Length() const { return fLength; }
546969690aSAxel Dörfler
55435c43f5SIngo Weinhold			generic_io_vec*		Vecs() { return fVecs; }
56435c43f5SIngo Weinhold			generic_io_vec&		VecAt(size_t index) { return fVecs[index]; }
5745a206a7SAxel Dörfler			size_t				VecCount() const { return fVecCount; }
586969690aSAxel Dörfler			size_t				Capacity() const { return fCapacity; }
596969690aSAxel Dörfler
60435c43f5SIngo Weinhold			status_t			GetNextVirtualVec(void*& cookie, iovec& vector);
6132e2b6a1SMichael Lotz			void				FreeVirtualVecCookie(void* cookie);
6232e2b6a1SMichael Lotz
630d4d5abeSIngo Weinhold			status_t			LockMemory(team_id team, bool isWrite);
640d4d5abeSIngo Weinhold			void				UnlockMemory(team_id team, bool isWrite);
6516d07755SIngo Weinhold			bool				IsMemoryLocked() const
6616d07755SIngo Weinhold									{ return fMemoryLocked; }
676969690aSAxel Dörfler
68f8a59924SAxel Dörfler			void				Dump() const;
69f8a59924SAxel Dörfler
706969690aSAxel Dörflerprivate:
717cc9a52dSIngo Weinhold								IOBuffer();
726969690aSAxel Dörfler								~IOBuffer();
736969690aSAxel Dörfler									// not implemented
740d4d5abeSIngo Weinhold			void				_UnlockMemory(team_id team, size_t count,
750d4d5abeSIngo Weinhold									bool isWrite);
766969690aSAxel Dörfler
776969690aSAxel Dörfler			bool				fUser;
786969690aSAxel Dörfler			bool				fPhysical;
7941417412SIngo Weinhold			bool				fVIP;
8016d07755SIngo Weinhold			bool				fMemoryLocked;
81435c43f5SIngo Weinhold			generic_size_t		fLength;
8245a206a7SAxel Dörfler			size_t				fVecCount;
836969690aSAxel Dörfler			size_t				fCapacity;
84435c43f5SIngo Weinhold			generic_io_vec		fVecs[1];
856969690aSAxel Dörfler};
866969690aSAxel Dörfler
876969690aSAxel Dörfler
8830c9d3c0SAugustin Cavalierstruct IORequest;
8930c9d3c0SAugustin Cavalierstruct IORequestOwner;
906969690aSAxel Dörfler
916969690aSAxel Dörfler
926969690aSAxel Dörflerclass IORequestChunk {
936969690aSAxel Dörflerpublic:
9445a206a7SAxel Dörfler								IORequestChunk();
956969690aSAxel Dörfler	virtual						~IORequestChunk();
966969690aSAxel Dörfler
976969690aSAxel Dörfler			IORequest*			Parent() const { return fParent; }
98902559ceSIngo Weinhold			void				SetParent(IORequest* parent)
99902559ceSIngo Weinhold									{ fParent = parent; }
1006969690aSAxel Dörfler
1016969690aSAxel Dörfler			status_t			Status() const { return fStatus; }
10209f0e0ecSIngo Weinhold
10309f0e0ecSIngo Weinhold			DoublyLinkedListLink<IORequestChunk>*
10409f0e0ecSIngo Weinhold									ListLink()	{ return &fListLink; }
10509f0e0ecSIngo Weinhold
10609f0e0ecSIngo Weinholdprotected:
1076969690aSAxel Dörfler			void				SetStatus(status_t status)
1086969690aSAxel Dörfler									{ fStatus = status; }
109902559ceSIngo Weinhold			void				ResetStatus()
110902559ceSIngo Weinhold									{ fStatus = 1; }
1116969690aSAxel Dörfler
1126969690aSAxel Dörflerprotected:
1136969690aSAxel Dörfler			IORequest*			fParent;
1146969690aSAxel Dörfler			status_t			fStatus;
1156969690aSAxel Dörfler
1166969690aSAxel Dörflerpublic:
1176969690aSAxel Dörfler			DoublyLinkedListLink<IORequestChunk> fListLink;
1186969690aSAxel Dörfler};
1196969690aSAxel Dörfler
1206969690aSAxel Dörflertypedef DoublyLinkedList<IORequestChunk,
1216969690aSAxel Dörfler	DoublyLinkedListMemberGetLink<IORequestChunk, &IORequestChunk::fListLink> >
1226969690aSAxel Dörfler		IORequestChunkList;
1236969690aSAxel Dörfler
1246969690aSAxel Dörfler
1256969690aSAxel Dörflerstruct IOOperation : IORequestChunk, DoublyLinkedListLinkImpl<IOOperation> {
1266969690aSAxel Dörflerpublic:
1276969690aSAxel Dörfler			bool				Finish();
1286969690aSAxel Dörfler									// returns true, if it can be recycled
1296969690aSAxel Dörfler
13086cea5c5SAxel Dörfler			status_t			Prepare(IORequest* request);
131435c43f5SIngo Weinhold			void				SetOriginalRange(off_t offset,
132435c43f5SIngo Weinhold									generic_size_t length);
1336969690aSAxel Dörfler									// also sets range
134435c43f5SIngo Weinhold			void				SetRange(off_t offset, generic_size_t length);
1356969690aSAxel Dörfler
13609f0e0ecSIngo Weinhold			void				SetStatus(status_t status)
13709f0e0ecSIngo Weinhold									{ IORequestChunk::SetStatus(status); }
13809f0e0ecSIngo Weinhold
1398faff60cSAxel Dörfler			off_t				Offset() const;
140435c43f5SIngo Weinhold			generic_size_t		Length() const;
1416969690aSAxel Dörfler			off_t				OriginalOffset() const
1426969690aSAxel Dörfler									{ return fOriginalOffset; }
143435c43f5SIngo Weinhold			generic_size_t		OriginalLength() const
1446969690aSAxel Dörfler									{ return fOriginalLength; }
1456969690aSAxel Dörfler
146435c43f5SIngo Weinhold			generic_size_t		TransferredBytes() const
1477f12cc54SIngo Weinhold									{ return fTransferredBytes; }
148435c43f5SIngo Weinhold			void				SetTransferredBytes(generic_size_t bytes)
1497f12cc54SIngo Weinhold									{ fTransferredBytes = bytes; }
1507f12cc54SIngo Weinhold
151435c43f5SIngo Weinhold			generic_io_vec*		Vecs() const;
15245a206a7SAxel Dörfler			uint32				VecCount() const;
15345a206a7SAxel Dörfler
15445a206a7SAxel Dörfler			void				SetPartial(bool partialBegin, bool partialEnd);
15545a206a7SAxel Dörfler			bool				HasPartialBegin() const
15645a206a7SAxel Dörfler									{ return fPartialBegin; }
15745a206a7SAxel Dörfler			bool				HasPartialEnd() const
15845a206a7SAxel Dörfler									{ return fPartialEnd; }
1596969690aSAxel Dörfler			bool				IsWrite() const;
1606969690aSAxel Dörfler			bool				IsRead() const;
1616969690aSAxel Dörfler
162435c43f5SIngo Weinhold			void				SetBlockSize(generic_size_t blockSize)
1638faff60cSAxel Dörfler									{ fBlockSize  = blockSize; }
1648faff60cSAxel Dörfler
1656969690aSAxel Dörfler			bool				UsesBounceBuffer() const
1668faff60cSAxel Dörfler									{ return fUsesBounceBuffer; }
1678faff60cSAxel Dörfler			void				SetUsesBounceBuffer(bool uses)
1688faff60cSAxel Dörfler									{ fUsesBounceBuffer = uses; }
1696969690aSAxel Dörfler
17045a206a7SAxel Dörfler			DMABuffer*			Buffer() const { return fDMABuffer; }
17145a206a7SAxel Dörfler			void				SetBuffer(DMABuffer* buffer)
17245a206a7SAxel Dörfler									{ fDMABuffer = buffer; }
17345a206a7SAxel Dörfler
174f8a59924SAxel Dörfler			void				Dump() const;
175f8a59924SAxel Dörfler
1766969690aSAxel Dörflerprotected:
1778faff60cSAxel Dörfler			void				_PrepareVecs();
17845a206a7SAxel Dörfler			status_t			_CopyPartialBegin(bool isWrite,
17945a206a7SAxel Dörfler									bool& partialBlockOnly);
18045a206a7SAxel Dörfler			status_t			_CopyPartialEnd(bool isWrite);
18145a206a7SAxel Dörfler
1826969690aSAxel Dörfler			DMABuffer*			fDMABuffer;
1836969690aSAxel Dörfler			off_t				fOffset;
1846969690aSAxel Dörfler			off_t				fOriginalOffset;
185435c43f5SIngo Weinhold			generic_size_t		fLength;
186435c43f5SIngo Weinhold			generic_size_t		fOriginalLength;
187435c43f5SIngo Weinhold			generic_size_t		fTransferredBytes;
188435c43f5SIngo Weinhold			generic_size_t		fBlockSize;
1898faff60cSAxel Dörfler			uint16				fSavedVecIndex;
1908faff60cSAxel Dörfler			uint16				fSavedVecLength;
1918faff60cSAxel Dörfler			uint8				fPhase;
19245a206a7SAxel Dörfler			bool				fPartialBegin;
19345a206a7SAxel Dörfler			bool				fPartialEnd;
1948faff60cSAxel Dörfler			bool				fUsesBounceBuffer;
1956969690aSAxel Dörfler};
1966969690aSAxel Dörfler
19709f0e0ecSIngo Weinhold
1986969690aSAxel Dörflertypedef IOOperation io_operation;
1996969690aSAxel Dörflertypedef DoublyLinkedList<IOOperation> IOOperationList;
2006969690aSAxel Dörfler
201902559ceSIngo Weinholdtypedef struct IORequest io_request;
20209f0e0ecSIngo Weinholdtypedef status_t (*io_request_finished_callback)(void* data,
2037f12cc54SIngo Weinhold			io_request* request, status_t status, bool partialTransfer,
204435c43f5SIngo Weinhold			generic_size_t transferEndOffset);
20580048d7dSIngo Weinhold			// TODO: Return type: status_t -> void
20609f0e0ecSIngo Weinholdtypedef status_t (*io_request_iterate_callback)(void* data,
2077f12cc54SIngo Weinhold			io_request* request, bool* _partialTransfer);
208902559ceSIngo Weinhold
2096969690aSAxel Dörfler
2106969690aSAxel Dörflerstruct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
2116969690aSAxel Dörfler								IORequest();
2126969690aSAxel Dörfler	virtual						~IORequest();
2136969690aSAxel Dörfler
21480048d7dSIngo Weinhold	static	IORequest*			Create(bool vip);
21580048d7dSIngo Weinhold
216435c43f5SIngo Weinhold			status_t			Init(off_t offset, generic_addr_t buffer,
217435c43f5SIngo Weinhold									generic_size_t length, bool write,
218435c43f5SIngo Weinhold									uint32 flags);
219435c43f5SIngo Weinhold			status_t			Init(off_t offset, const generic_io_vec* vecs,
220435c43f5SIngo Weinhold									size_t count, generic_size_t length,
221435c43f5SIngo Weinhold									bool write, uint32 flags)
22209f0e0ecSIngo Weinhold									{ return Init(offset, 0, vecs, count,
22309f0e0ecSIngo Weinhold										length, write, flags); }
224435c43f5SIngo Weinhold			status_t			Init(off_t offset,
225435c43f5SIngo Weinhold									generic_size_t firstVecOffset,
226435c43f5SIngo Weinhold									const generic_io_vec* vecs, size_t count,
227435c43f5SIngo Weinhold									generic_size_t length, bool write,
228435c43f5SIngo Weinhold									uint32 flags);
22909f0e0ecSIngo Weinhold
2300316483fSIngo Weinhold			void				SetOwner(IORequestOwner* owner)
2310316483fSIngo Weinhold									{ fOwner = owner; }
2320316483fSIngo Weinhold			IORequestOwner*		Owner() const	{ return fOwner; }
2330316483fSIngo Weinhold
23409f0e0ecSIngo Weinhold			status_t			CreateSubRequest(off_t parentOffset,
235435c43f5SIngo Weinhold									off_t offset, generic_size_t length,
23609f0e0ecSIngo Weinhold									IORequest*& subRequest);
23709f0e0ecSIngo Weinhold			void				DeleteSubRequests();
23845a206a7SAxel Dörfler
239902559ceSIngo Weinhold			void				SetFinishedCallback(
24009f0e0ecSIngo Weinhold									io_request_finished_callback callback,
2418c980485SIngo Weinhold									void* cookie);
2428c980485SIngo Weinhold			void				SetIterationCallback(
24309f0e0ecSIngo Weinhold									io_request_iterate_callback callback,
244902559ceSIngo Weinhold									void* cookie);
24509f0e0ecSIngo Weinhold			io_request_finished_callback FinishedCallback(
24609f0e0ecSIngo Weinhold									void** _cookie = NULL) const;
247902559ceSIngo Weinhold
248902559ceSIngo Weinhold			status_t			Wait(uint32 flags = 0, bigtime_t timeout = 0);
249902559ceSIngo Weinhold
2508c980485SIngo Weinhold			bool				IsFinished() const
2518c980485SIngo Weinhold									{ return fStatus != 1
2528c980485SIngo Weinhold										&& fPendingChildren == 0; }
2538c980485SIngo Weinhold			void				NotifyFinished();
2548c980485SIngo Weinhold			bool				HasCallbacks() const;
25509f0e0ecSIngo Weinhold			void				SetStatusAndNotify(status_t status);
256902559ceSIngo Weinhold
2578c980485SIngo Weinhold			void				OperationFinished(IOOperation* operation,
2587f12cc54SIngo Weinhold									status_t status, bool partialTransfer,
259435c43f5SIngo Weinhold									generic_size_t transferEndOffset);
26009f0e0ecSIngo Weinhold			void				SubRequestFinished(IORequest* request,
2617f12cc54SIngo Weinhold									status_t status, bool partialTransfer,
262435c43f5SIngo Weinhold									generic_size_t transferEndOffset);
2630316483fSIngo Weinhold			void				SetUnfinished();
2646969690aSAxel Dörfler
265435c43f5SIngo Weinhold			generic_size_t		RemainingBytes() const
2666969690aSAxel Dörfler									{ return fRemainingBytes; }
267435c43f5SIngo Weinhold			generic_size_t		TransferredBytes() const
2687f12cc54SIngo Weinhold									{ return fTransferSize; }
2697f12cc54SIngo Weinhold			bool				IsPartialTransfer() const
2707f12cc54SIngo Weinhold									{ return fPartialTransfer; }
2717f12cc54SIngo Weinhold			void				SetTransferredBytes(bool partialTransfer,
272435c43f5SIngo Weinhold									generic_size_t transferredBytes);
2736969690aSAxel Dörfler
274c3941400SMichael Lotz			void				SetSuppressChildNotifications(bool suppress);
275c3941400SMichael Lotz			bool				SuppressChildNotifications() const
276c3941400SMichael Lotz									{ return fSuppressChildNotifications; }
277c3941400SMichael Lotz
2786969690aSAxel Dörfler			bool				IsWrite() const	{ return fIsWrite; }
2796969690aSAxel Dörfler			bool				IsRead() const	{ return !fIsWrite; }
2804535495dSIngo Weinhold			team_id				TeamID() const		{ return fTeam; }
2814535495dSIngo Weinhold			thread_id			ThreadID() const	{ return fThread; }
28241417412SIngo Weinhold			uint32				Flags() const	{ return fFlags; }
2836969690aSAxel Dörfler
2846969690aSAxel Dörfler			IOBuffer*			Buffer() const	{ return fBuffer; }
2856969690aSAxel Dörfler			off_t				Offset() const	{ return fOffset; }
286435c43f5SIngo Weinhold			generic_size_t		Length() const	{ return fLength; }
2876969690aSAxel Dörfler
2880ff0056dSIngo Weinhold			void				SetOffset(off_t offset)	{ fOffset = offset; }
2890ff0056dSIngo Weinhold
29045a206a7SAxel Dörfler			uint32				VecIndex() const	{ return fVecIndex; }
291435c43f5SIngo Weinhold			generic_size_t		VecOffset() const	{ return fVecOffset; }
29245a206a7SAxel Dörfler
293435c43f5SIngo Weinhold			void				Advance(generic_size_t bySize);
2946969690aSAxel Dörfler
29509f0e0ecSIngo Weinhold			IORequest*			FirstSubRequest();
29609f0e0ecSIngo Weinhold			IORequest*			NextSubRequest(IORequest* previous);
29709f0e0ecSIngo Weinhold
2986969690aSAxel Dörfler			void				AddOperation(IOOperation* operation);
2996969690aSAxel Dörfler			void				RemoveOperation(IOOperation* operation);
3006969690aSAxel Dörfler
3016969690aSAxel Dörfler			status_t			CopyData(off_t offset, void* buffer,
3026969690aSAxel Dörfler									size_t size);
3036969690aSAxel Dörfler			status_t			CopyData(const void* buffer, off_t offset,
3046969690aSAxel Dörfler									size_t size);
305c63b3de6SIngo Weinhold			status_t			ClearData(off_t offset, generic_size_t size);
3066969690aSAxel Dörfler
307f8a59924SAxel Dörfler			void				Dump() const;
308f8a59924SAxel Dörfler
3096969690aSAxel Dörflerprivate:
3106969690aSAxel Dörfler			status_t			_CopyData(void* buffer, off_t offset,
3116969690aSAxel Dörfler									size_t size, bool copyIn);
31214a322f3SIngo Weinhold	static	status_t			_CopySimple(void* bounceBuffer,
31314a322f3SIngo Weinhold									generic_addr_t external, size_t size,
31414a322f3SIngo Weinhold									team_id team, bool copyIn);
3156969690aSAxel Dörfler	static	status_t			_CopyPhysical(void* bounceBuffer,
31614a322f3SIngo Weinhold									generic_addr_t external, size_t size,
31714a322f3SIngo Weinhold									team_id team, bool copyIn);
31814a322f3SIngo Weinhold	static	status_t			_CopyUser(void* bounceBuffer,
31914a322f3SIngo Weinhold									generic_addr_t external, size_t size,
32014a322f3SIngo Weinhold									team_id team, bool copyIn);
321c63b3de6SIngo Weinhold	static	status_t			_ClearDataSimple(generic_addr_t external,
322c63b3de6SIngo Weinhold									generic_size_t size, team_id team);
323c63b3de6SIngo Weinhold	static	status_t			_ClearDataPhysical(generic_addr_t external,
324c63b3de6SIngo Weinhold									generic_size_t size, team_id team);
325c63b3de6SIngo Weinhold	static	status_t			_ClearDataUser(generic_addr_t external,
326c63b3de6SIngo Weinhold									generic_size_t size, team_id team);
3276969690aSAxel Dörfler
328902559ceSIngo Weinhold			mutex				fLock;
3290316483fSIngo Weinhold			IORequestOwner*		fOwner;
3306969690aSAxel Dörfler			IOBuffer*			fBuffer;
3316969690aSAxel Dörfler			off_t				fOffset;
332435c43f5SIngo Weinhold			generic_size_t		fLength;
333435c43f5SIngo Weinhold			generic_size_t		fTransferSize;
3347f12cc54SIngo Weinhold									// After all subrequests/operations have
3357f12cc54SIngo Weinhold									// finished, number of contiguous bytes at
3367f12cc54SIngo Weinhold									// the beginning of the request that have
3377f12cc54SIngo Weinhold									// actually been transferred.
338435c43f5SIngo Weinhold			generic_size_t		fRelativeParentOffset;
3397f12cc54SIngo Weinhold									// offset of this request relative to its
3407f12cc54SIngo Weinhold									// parent
3416969690aSAxel Dörfler			IORequestChunkList	fChildren;
342902559ceSIngo Weinhold			int32				fPendingChildren;
3436969690aSAxel Dörfler			uint32				fFlags;
3446969690aSAxel Dörfler			team_id				fTeam;
345841b6cd7SIngo Weinhold			thread_id			fThread;
3466969690aSAxel Dörfler			bool				fIsWrite;
3477f12cc54SIngo Weinhold			bool				fPartialTransfer;
348c3941400SMichael Lotz			bool				fSuppressChildNotifications;
3492f7eb9b5SIngo Weinhold			bool				fIsNotified;
3506969690aSAxel Dörfler
35109f0e0ecSIngo Weinhold			io_request_finished_callback	fFinishedCallback;
352902559ceSIngo Weinhold			void*				fFinishedCookie;
35309f0e0ecSIngo Weinhold			io_request_iterate_callback	fIterationCallback;
3548c980485SIngo Weinhold			void*				fIterationCookie;
355902559ceSIngo Weinhold			ConditionVariable	fFinishedCondition;
356902559ceSIngo Weinhold
3576969690aSAxel Dörfler			// these are for iteration
3586969690aSAxel Dörfler			uint32				fVecIndex;
359435c43f5SIngo Weinhold			generic_size_t		fVecOffset;
360435c43f5SIngo Weinhold			generic_size_t		fRemainingBytes;
3616969690aSAxel Dörfler};
3626969690aSAxel Dörfler
3636969690aSAxel Dörfler
3646969690aSAxel Dörflertypedef DoublyLinkedList<IORequest> IORequestList;
3656969690aSAxel Dörfler
3666969690aSAxel Dörfler
367aa4ba93eSIngo Weinhold#endif	// IO_REQUEST_H
368