1/*
2** Copyright 2002/03, Thomas Kurschel. All rights reserved.
3** Distributed under the terms of the MIT License.
4*/
5
6/*
7	Part of Open SCSI bus manager
8
9	Handling of bus/device blocking. Inline functions defined
10	here don't wake service thread if required and don't lock bus, so
11	everyone using them must take care of that himself.
12*/
13
14#ifndef __BLOCKING_H__
15#define __BLOCKING_H__
16
17#include "dl_list.h"
18
19void scsi_add_queued_request( scsi_ccb *request );
20void scsi_add_queued_request_first( scsi_ccb *request );
21void scsi_remove_queued_request( scsi_ccb *request );
22
23
24static inline void scsi_add_req_queue_first( scsi_ccb *request )
25{
26	scsi_device_info *device = request->device;
27
28	SHOW_FLOW( 3, "request=%p", request );
29
30	ADD_CDL_LIST_HEAD( request, scsi_ccb, device->queued_reqs, );
31}
32
33static inline void scsi_add_req_queue_last( scsi_ccb *request )
34{
35	scsi_device_info *device = request->device;
36
37	SHOW_FLOW( 3, "request=%p", request );
38
39	ADD_CDL_LIST_TAIL( request, scsi_ccb, device->queued_reqs, );
40}
41
42static inline void scsi_remove_req_queue( scsi_ccb *request )
43{
44	scsi_device_info *device = request->device;
45
46	SHOW_FLOW( 3, "request=%p", request );
47
48	REMOVE_CDL_LIST( request, device->queued_reqs, );
49}
50
51// ignored, if device is already queued
52static inline void scsi_add_device_queue_first( scsi_device_info *device )
53{
54	SHOW_FLOW0( 3, "" );
55
56	if( DEVICE_IN_WAIT_QUEUE( device ))
57		return;
58
59	SHOW_FLOW0( 3, "was not in wait queue - adding" );
60
61	ADD_CDL_LIST_HEAD( device, scsi_device_info, device->bus->waiting_devices, waiting_ );
62}
63
64// ignored, if device is already queued
65static inline void scsi_add_device_queue_last( scsi_device_info *device )
66{
67	SHOW_FLOW0( 3, "" );
68
69	if( DEVICE_IN_WAIT_QUEUE( device ))
70		return;
71
72	SHOW_FLOW0( 3, "was not in wait queue - adding" );
73
74	ADD_CDL_LIST_TAIL( device, scsi_device_info, device->bus->waiting_devices, waiting_ );
75}
76
77// ignored, if device is not in queue
78static inline void scsi_remove_device_queue( scsi_device_info *device )
79{
80	SHOW_FLOW0( 3, "" );
81
82	if( !DEVICE_IN_WAIT_QUEUE( device ))
83		return;
84
85	SHOW_FLOW0( 3, "was in wait queue - removing from it" );
86
87	REMOVE_CDL_LIST( device, device->bus->waiting_devices, waiting_ );
88
89	// reset next link so we can see that it's not in device queue
90	device->waiting_next = NULL;
91}
92
93// set overflow bit of device; this will not remove device from bus queue!
94// (multiple calls are ignored gracefully)
95static inline void scsi_set_device_overflow( scsi_device_info *device )
96{
97	device->lock_count += device->sim_overflow ^ 1;
98	device->sim_overflow = 1;
99}
100
101// set overflow bit of bus
102// (multiple calls are ignored gracefully)
103static inline void scsi_set_bus_overflow( scsi_bus_info *bus )
104{
105	bus->lock_count += bus->sim_overflow ^ 1;
106	bus->sim_overflow = 1;
107}
108
109// clear overflow bit of device; this will not add device to bus queue!
110// (multiple calls are ignored gracefully)
111static inline void scsi_clear_device_overflow( scsi_device_info *device )
112{
113	device->lock_count -= device->sim_overflow;
114	device->sim_overflow = 0;
115}
116
117// clear overflow bit of bus
118// (multiple calls are ignored gracefully)
119static inline void scsi_clear_bus_overflow( scsi_bus_info *bus )
120{
121	bus->lock_count -= bus->sim_overflow;
122	bus->sim_overflow = 0;
123}
124
125// check whether bus has some pending requests it can process now
126static inline bool scsi_can_service_bus( scsi_bus_info *bus )
127{
128	// bus must not be blocked and requests pending
129	return (bus->lock_count == 0) & (bus->waiting_devices != NULL);
130}
131
132// unblock bus
133// lock must be hold; service thread is not informed
134static inline void scsi_unblock_bus_noresume( scsi_bus_info *bus, bool by_SIM )
135{
136	if( bus->blocked[by_SIM] > 0 ) {
137		--bus->blocked[by_SIM];
138		--bus->lock_count;
139	} else {
140		panic( "Tried to unblock bus %d which wasn't blocked",
141			bus->path_id );
142	}
143}
144
145
146// unblock device
147// lock must be hold; device is not added to queue and service thread is not informed
148static inline void scsi_unblock_device_noresume( scsi_device_info *device, bool by_SIM )
149{
150	if( device->blocked[by_SIM] > 0 ) {
151		--device->blocked[by_SIM];
152		--device->lock_count;
153	} else {
154		panic( "Tried to unblock device %d/%d/%d which wasn't blocked",
155			device->bus->path_id, device->target_id, device->target_lun );
156	}
157}
158
159
160// block bus
161// lock must be hold
162static inline void scsi_block_bus_nolock( scsi_bus_info *bus, bool by_SIM )
163{
164	++bus->blocked[by_SIM];
165	++bus->lock_count;
166}
167
168
169// block device
170// lock must be hold
171static inline void scsi_block_device_nolock( scsi_device_info *device, bool by_SIM )
172{
173	++device->blocked[by_SIM];
174	++device->lock_count;
175
176	// remove device from bus queue as it cannot be processed anymore
177	scsi_remove_device_queue( device );
178}
179
180
181void scsi_block_bus( scsi_bus_info *bus );
182void scsi_unblock_bus( scsi_bus_info *bus );
183void scsi_block_device( scsi_device_info *device );
184void scsi_unblock_device( scsi_device_info *device );
185
186void scsi_cont_send_bus( scsi_bus_info *bus );
187void scsi_cont_send_device( scsi_device_info *device );
188
189#endif
190