12db0303fSimker /**
22db0303fSimker *
32db0303fSimker * TODO: description
42db0303fSimker *
5*06437987SMatt Madia * This file is a part of USB SCSI CAM for Haiku.
62db0303fSimker * May be used under terms of the MIT License
72db0303fSimker *
82db0303fSimker * Author(s):
92db0303fSimker * Siarzhuk Zharski <imker@gmx.li>
102db0303fSimker *
11b3d94504SStephan Aßmus *
12b3d94504SStephan Aßmus */
13b3d94504SStephan Aßmus /** driver settings support implementation */
14b3d94504SStephan Aßmus
15b3d94504SStephan Aßmus #include "usb_scsi.h"
16b3d94504SStephan Aßmus #include "settings.h"
17b3d94504SStephan Aßmus //#include "proto_common.h"
18b3d94504SStephan Aßmus
19b3d94504SStephan Aßmus #include <stdlib.h> /* strtoul */
20b3d94504SStephan Aßmus #include <strings.h> /* strncpy */
21b3d94504SStephan Aßmus #include <driver_settings.h>
22b3d94504SStephan Aßmus #include "tracing.h"
23b3d94504SStephan Aßmus
24b3d94504SStephan Aßmus #define DEF_DEVS 2 /**< default amount of reserved devices */
25b3d94504SStephan Aßmus #define S_DEF_DEVS "2" /**< default amount of reserved devices */
26b3d94504SStephan Aßmus #define DEF_LUNS 4 /**< default amount of reserved LUNs */
27b3d94504SStephan Aßmus #define S_DEF_LUNS "4" /**< default amount of reserved LUNs */
28b3d94504SStephan Aßmus /** count of device entries, to be reserved for fake devices */
29b3d94504SStephan Aßmus int reserved_devices = DEF_DEVS;
30b3d94504SStephan Aßmus /** count of Logical Unit Numbers, to be reserved for fake devices */
31b3d94504SStephan Aßmus int reserved_luns = DEF_LUNS;
32b3d94504SStephan Aßmus
33b3d94504SStephan Aßmus bool b_reservation_on = true;
34b3d94504SStephan Aßmus bool b_ignore_sysinit2 = false;
35b3d94504SStephan Aßmus /**
36b3d94504SStephan Aßmus support of some kind rudimentary "quick" search. Indexes in
37b3d94504SStephan Aßmus settings_keys array.
38b3d94504SStephan Aßmus */
39b3d94504SStephan Aßmus enum SKKeys{
40b3d94504SStephan Aßmus skkVendor = 0,
41b3d94504SStephan Aßmus skkDevice,
42b3d94504SStephan Aßmus // skkName,
43b3d94504SStephan Aßmus // skkTransport,
44b3d94504SStephan Aßmus skkProtocol,
45b3d94504SStephan Aßmus skkCommandSet,
46b3d94504SStephan Aßmus skkFakeInq,
47b3d94504SStephan Aßmus skk6ByteCmd,
48b3d94504SStephan Aßmus skkTransTU,
49b3d94504SStephan Aßmus skkNoTU,
50b3d94504SStephan Aßmus skkNoGetMaxLUN,
51b3d94504SStephan Aßmus skkNoPreventMedia,
52b3d94504SStephan Aßmus skkUseModeSense10,
53b3d94504SStephan Aßmus skkForceReadOnly,
54b3d94504SStephan Aßmus skkProtoBulk,
55b3d94504SStephan Aßmus skkProtoCB,
56b3d94504SStephan Aßmus skkProtoCBI,
57b3d94504SStephan Aßmus // skkProtoFreecom,
58b3d94504SStephan Aßmus skkCmdSetSCSI,
59b3d94504SStephan Aßmus skkCmdSetUFI,
60b3d94504SStephan Aßmus skkCmdSetATAPI,
61b3d94504SStephan Aßmus skkCmdSetRBC,
62b3d94504SStephan Aßmus skkCmdSetQIC157,
63b3d94504SStephan Aßmus
64b3d94504SStephan Aßmus skkKeysCount,
65b3d94504SStephan Aßmus // skkTransportBase = skkSubClassSCSI,
66b3d94504SStephan Aßmus skkProtoBegin = skkProtoBulk,
67b3d94504SStephan Aßmus skkProtoEnd = skkProtoCBI,
68b3d94504SStephan Aßmus skkCmdSetBegin = skkCmdSetSCSI,
69b3d94504SStephan Aßmus skkCmdSetEnd = skkCmdSetQIC157,
70b3d94504SStephan Aßmus };
71b3d94504SStephan Aßmus /**
72b3d94504SStephan Aßmus helper struct, used in our "quick" search algorithm
73b3d94504SStephan Aßmus */
74b3d94504SStephan Aßmus struct _settings_key{
75b3d94504SStephan Aßmus union _hash{
76b3d94504SStephan Aßmus char name[32];
77b3d94504SStephan Aßmus uint16 key;
78b3d94504SStephan Aßmus }hash;
79b3d94504SStephan Aßmus uint32 property;
80b3d94504SStephan Aßmus }settings_keys[] = { /**< array of keys, used in our settings files */
81b3d94504SStephan Aßmus {{"vendor" }, 0}, /* MUST BE SYNCHRONISED WITH skk*** indexes!!! */
82b3d94504SStephan Aßmus {{"device" }, 0},
83b3d94504SStephan Aßmus // {{"name" }, 0},
84b3d94504SStephan Aßmus // {{"transport"}, 0},
85b3d94504SStephan Aßmus {{"protocol"}, 0},
86b3d94504SStephan Aßmus {{"commandset"}, 0},
87b3d94504SStephan Aßmus {{"fake_inquiry" }, 0},
88b3d94504SStephan Aßmus {{"use_rw6_byte_cmd" }, 0},
89b3d94504SStephan Aßmus {{"trans_test_unit" }, 0},
90b3d94504SStephan Aßmus {{"no_test_unit" }, 0},
91b3d94504SStephan Aßmus {{"no_get_max_lun"}, 0},
92b3d94504SStephan Aßmus {{"no_prevent_media"}, 0},
93b3d94504SStephan Aßmus {{"use_mode_sense_10"}, 0},
94b3d94504SStephan Aßmus {{"force_read_only"}, 0},
95b3d94504SStephan Aßmus {{"BULK" }, PROTO_BULK_ONLY},
96b3d94504SStephan Aßmus {{"CB" }, PROTO_CB},
97b3d94504SStephan Aßmus {{"CBI" }, PROTO_CBI},
98b3d94504SStephan Aßmus // {{"Freecom"}, PROTO_FREECOM},
99b3d94504SStephan Aßmus {{"SCSI" }, CMDSET_SCSI},
100b3d94504SStephan Aßmus {{"UFI" }, CMDSET_UFI},
101b3d94504SStephan Aßmus {{"ATAPI" }, CMDSET_ATAPI},
102b3d94504SStephan Aßmus {{"RBC" }, CMDSET_RBC},
103b3d94504SStephan Aßmus {{"QIC157" }, CMDSET_QIC157},
104b3d94504SStephan Aßmus };
105b3d94504SStephan Aßmus /**
106b3d94504SStephan Aßmus \define:SK_EQUAL
107b3d94504SStephan Aßmus checks is the __name parameter correspond to value, pointed by __id index
108b3d94504SStephan Aßmus in settings_keys array. The magic of our "quick" search algorithm =-))
109b3d94504SStephan Aßmus */
110b3d94504SStephan Aßmus #define CAST_SK(__name) (*(uint16 *)(__name))
111b3d94504SStephan Aßmus #define SK_EQUAL(__name, __id) ((CAST_SK(__name) == (settings_keys[__id].hash.key)) && \
112b3d94504SStephan Aßmus (0 == strcmp(__name, settings_keys[__id].hash.name)))
113b3d94504SStephan Aßmus /**
114b3d94504SStephan Aßmus \fn:load_module_settings
115b3d94504SStephan Aßmus loads driver settings from extarnal settings file through BeOS driver
116b3d94504SStephan Aßmus settings API. Called on initialization of the module
117b3d94504SStephan Aßmus */
load_module_settings(void)1182db0303fSimker void load_module_settings(void)
119b3d94504SStephan Aßmus {
120b3d94504SStephan Aßmus void *sh = load_driver_settings(MODULE_NAME);
121b3d94504SStephan Aßmus if(sh){
122b3d94504SStephan Aßmus load_log_settings(sh);
123b3d94504SStephan Aßmus /* devices "reservation". Workaround for plug-n-play device attaching*/
124b3d94504SStephan Aßmus reserved_devices = strtoul(get_driver_parameter(sh, "reserve_devices",
125b3d94504SStephan Aßmus S_DEF_DEVS, S_DEF_DEVS), NULL, 0);
126b3d94504SStephan Aßmus reserved_luns = strtoul(get_driver_parameter(sh, "reserve_luns",
127b3d94504SStephan Aßmus S_DEF_LUNS, S_DEF_LUNS), NULL, 0);
128b3d94504SStephan Aßmus b_ignore_sysinit2 = get_driver_boolean_parameter(sh, "ignore_sysinit2",
129b3d94504SStephan Aßmus b_ignore_sysinit2, false);
130b3d94504SStephan Aßmus if(reserved_devices > MAX_DEVICES_COUNT)
131b3d94504SStephan Aßmus reserved_devices = MAX_DEVICES_COUNT;
132b3d94504SStephan Aßmus if(reserved_luns > MAX_LUNS_COUNT)
133b3d94504SStephan Aßmus reserved_luns = MAX_LUNS_COUNT;
134b3d94504SStephan Aßmus b_reservation_on = (reserved_devices != 0);
135b3d94504SStephan Aßmus
136b3d94504SStephan Aßmus unload_driver_settings(sh);
137b3d94504SStephan Aßmus } else {
138b3d94504SStephan Aßmus TRACE("settings:load:file '%s' was not found. Using default setting...\n",
139b3d94504SStephan Aßmus MODULE_NAME);
140b3d94504SStephan Aßmus }
141b3d94504SStephan Aßmus }
142b3d94504SStephan Aßmus /**
143b3d94504SStephan Aßmus \fn:strncpy_value
144b3d94504SStephan Aßmus \param to: buffer for copied string
145b3d94504SStephan Aßmus \param dp: driver_parameter, from wich copied string come
146b3d94504SStephan Aßmus \param size: maximal size of copied string
147b3d94504SStephan Aßmus copies a string, containing value[0] of this parameter, from driver_parameter,
148b3d94504SStephan Aßmus pointed by dp, to buffer pointed by to. Semantic of this function is similar
149b3d94504SStephan Aßmus to standard strncpy() one.
150b3d94504SStephan Aßmus */
151b3d94504SStephan Aßmus /*static void
152b3d94504SStephan Aßmus strncpy_value(char *to, driver_parameter *dp, size_t size)
153b3d94504SStephan Aßmus {
154b3d94504SStephan Aßmus to[0] = 0;
155b3d94504SStephan Aßmus if(dp->value_count > 0){
156b3d94504SStephan Aßmus strncpy(to, dp->values[0], size);
157b3d94504SStephan Aßmus }
158b3d94504SStephan Aßmus }*/
159b3d94504SStephan Aßmus /**
160b3d94504SStephan Aßmus \fn:parse_transport
161b3d94504SStephan Aßmus \param dp: driver_parameter, containing device transport information
162b3d94504SStephan Aßmus \return: a bitmasked value from PROP_-defined flags for USB subclass and \
163b3d94504SStephan Aßmus protocol
164b3d94504SStephan Aßmus parse the transport driver_parameter for known USB subclasses, protocols and
165b3d94504SStephan Aßmus compose a bitmasked value from those settings
166b3d94504SStephan Aßmus */
167b3d94504SStephan Aßmus static uint32
parse_transport(driver_parameter * dp,int skkBase,int skkEnd,uint32 vendor_prop,char * vendor_prop_name)1682db0303fSimker parse_transport(driver_parameter *dp, int skkBase, int skkEnd,
169b3d94504SStephan Aßmus uint32 vendor_prop, char *vendor_prop_name)
170b3d94504SStephan Aßmus {
171b3d94504SStephan Aßmus uint32 ret = 0;
172b3d94504SStephan Aßmus if(dp->value_count > 0){
173b3d94504SStephan Aßmus char *value = dp->values[0];
174b3d94504SStephan Aßmus int skkIdx = skkBase;
175b3d94504SStephan Aßmus for(; skkIdx <= skkEnd; skkIdx++){
176b3d94504SStephan Aßmus if(SK_EQUAL(value, skkIdx)){
177b3d94504SStephan Aßmus ret |= settings_keys[skkIdx].property;
178b3d94504SStephan Aßmus break;
179b3d94504SStephan Aßmus }
180b3d94504SStephan Aßmus } /* for(...) enumerate protocol and commandset keys */
181b3d94504SStephan Aßmus if(skkIdx > skkEnd){ /* not found - assume vendor prop */
182b3d94504SStephan Aßmus ret |= vendor_prop;
183b3d94504SStephan Aßmus strncpy(vendor_prop_name, value, VENDOR_PROP_NAME_LEN);
184b3d94504SStephan Aßmus }
185b3d94504SStephan Aßmus if(dp->value_count > 1){
186b3d94504SStephan Aßmus TRACE("settings:parse_transport:accept '%s', ignore extra...\n", value);
187b3d94504SStephan Aßmus }
188b3d94504SStephan Aßmus }
189b3d94504SStephan Aßmus return ret;
190b3d94504SStephan Aßmus }
191b3d94504SStephan Aßmus /**
192b3d94504SStephan Aßmus \fn:lookup_device_info
193b3d94504SStephan Aßmus \param product_id: product id of device to be checked for private settings
194b3d94504SStephan Aßmus \param dp: driver_parameter, containing device information
195b3d94504SStephan Aßmus \param udd: on return contains name,protocol etc. information about device
196b3d94504SStephan Aßmus \return: "true" if private settings for device found - "false" otherwise
197b3d94504SStephan Aßmus looks through device parameter, pointed by dp, obtains the name and other
198b3d94504SStephan Aßmus parameters of private device settings if available
199b3d94504SStephan Aßmus */
200b3d94504SStephan Aßmus static bool
lookup_device_info(uint16 product_id,driver_parameter * dp,usb_device_settings * uds)2012db0303fSimker lookup_device_info(uint16 product_id, driver_parameter *dp,
202b3d94504SStephan Aßmus usb_device_settings *uds)
203b3d94504SStephan Aßmus {
204b3d94504SStephan Aßmus bool b_found = false;
205b3d94504SStephan Aßmus if(dp){
206b3d94504SStephan Aßmus int i = 0;
207b3d94504SStephan Aßmus for(; i < dp->value_count; i++){
208b3d94504SStephan Aßmus uint16 id = strtoul(dp->values[0], NULL, 0) & 0xffff;
209b3d94504SStephan Aßmus if(product_id == id){
210b3d94504SStephan Aßmus int prm = 0;
211b3d94504SStephan Aßmus uds->product_id = product_id;
212b3d94504SStephan Aßmus for(; prm < dp->parameter_count; prm++){
213b3d94504SStephan Aßmus /* if(SK_EQUAL(dp->parameters[prm].name, skkName)){
214b3d94504SStephan Aßmus strncpy_value(udd->product_name, &dp->parameters[prm], INQ_PRODUCT_LEN);
215b3d94504SStephan Aßmus } else*/
216b3d94504SStephan Aßmus /* if(SK_EQUAL(dp->parameters[prm].name, skkTransport)){
217b3d94504SStephan Aßmus udd->properties |= parse_transport(&dp->parameters[prm]);
218b3d94504SStephan Aßmus } else*/
219b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkProtocol)){
220b3d94504SStephan Aßmus uds->properties |= parse_transport(&dp->parameters[prm],
221b3d94504SStephan Aßmus skkProtoBegin, skkProtoEnd,
2222db0303fSimker PROTO_VENDOR, uds->vendor_protocol);
223b3d94504SStephan Aßmus } else
224b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkCommandSet)){
225b3d94504SStephan Aßmus uds->properties |= parse_transport(&dp->parameters[prm],
226b3d94504SStephan Aßmus skkCmdSetBegin, skkCmdSetEnd,
2272db0303fSimker CMDSET_VENDOR, uds->vendor_commandset);
228b3d94504SStephan Aßmus } else
229b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkFakeInq)){
230b3d94504SStephan Aßmus uds->properties |= FIX_NO_INQUIRY;
231b3d94504SStephan Aßmus } else
232b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skk6ByteCmd)){
233b3d94504SStephan Aßmus uds->properties |= FIX_FORCE_RW_TO_6;
234b3d94504SStephan Aßmus } else
235b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkTransTU)){
236b3d94504SStephan Aßmus uds->properties |= FIX_TRANS_TEST_UNIT;
237b3d94504SStephan Aßmus } else
238b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkNoTU)){
239b3d94504SStephan Aßmus uds->properties |= FIX_NO_TEST_UNIT;
240b3d94504SStephan Aßmus } else
241b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkNoPreventMedia)){
242b3d94504SStephan Aßmus uds->properties |= FIX_NO_PREVENT_MEDIA;
243b3d94504SStephan Aßmus } else
244b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkUseModeSense10)){
245b3d94504SStephan Aßmus uds->properties |= FIX_FORCE_MS_TO_10;
246b3d94504SStephan Aßmus } else
247b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkForceReadOnly)){
248b3d94504SStephan Aßmus uds->properties |= FIX_FORCE_READ_ONLY;
249b3d94504SStephan Aßmus } else
250b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[prm].name, skkNoGetMaxLUN)){
251b3d94504SStephan Aßmus uds->properties |= FIX_NO_GETMAXLUN;
252b3d94504SStephan Aßmus } else {
253b3d94504SStephan Aßmus TRACE("settings:device:ignore unknown parameter:%s\n",
254b3d94504SStephan Aßmus dp->parameters[prm].name);
255b3d94504SStephan Aßmus }
256b3d94504SStephan Aßmus } /* for(...) enumerate device parameters */
257b3d94504SStephan Aßmus b_found = true;
258b3d94504SStephan Aßmus break;
259b3d94504SStephan Aßmus } /* if(product_id == id){ */
260b3d94504SStephan Aßmus } /*enumerate parameter values (product ids) */
261b3d94504SStephan Aßmus } /* if(dp) */
262b3d94504SStephan Aßmus return b_found;
263b3d94504SStephan Aßmus }
264b3d94504SStephan Aßmus /**
265b3d94504SStephan Aßmus \fn:lookup_vendor_info
266b3d94504SStephan Aßmus \param vendor_id: vendor id of device to be checked for private settings
267b3d94504SStephan Aßmus \param product_id: product id of device to be checked for private settings
268b3d94504SStephan Aßmus \param dp: driver_parameter, containing vendor information
269b3d94504SStephan Aßmus \param udd: on return contains name, protocol etc. information about device
270b3d94504SStephan Aßmus \return: "true" if private settings for device found - "false" otherwise
271b3d94504SStephan Aßmus looks through vendor parameter, pointed by dp, obtains the name of vendor and
272b3d94504SStephan Aßmus device information if available
273b3d94504SStephan Aßmus */
274b3d94504SStephan Aßmus static bool
lookup_vendor_info(uint16 vendor_id,uint16 product_id,driver_parameter * dp,usb_device_settings * uds)2752db0303fSimker lookup_vendor_info(uint16 vendor_id, uint16 product_id,
2762db0303fSimker driver_parameter *dp, usb_device_settings *uds)
277b3d94504SStephan Aßmus {
278b3d94504SStephan Aßmus bool b_found = false;
279b3d94504SStephan Aßmus if(dp && dp->value_count > 0 && dp->values[0]){
280b3d94504SStephan Aßmus uint16 id = strtoul(dp->values[0], NULL, 0) & 0xffff;
281b3d94504SStephan Aßmus if(vendor_id == id){
282b3d94504SStephan Aßmus int i = 0;
283b3d94504SStephan Aßmus for( i = 0; i < dp->parameter_count; i++){
284b3d94504SStephan Aßmus if(!b_found && SK_EQUAL(dp->parameters[i].name, skkDevice)){
285b3d94504SStephan Aßmus b_found = lookup_device_info(product_id, &dp->parameters[i], uds);
286b3d94504SStephan Aßmus } /*else
287b3d94504SStephan Aßmus if(SK_EQUAL(dp->parameters[i].name, skkName)){
288b3d94504SStephan Aßmus strncpy_value(udd->vendor_name, &dp->parameters[i], INQ_VENDOR_LEN);
289b3d94504SStephan Aßmus } */else {
290b3d94504SStephan Aßmus TRACE("settings:vendor:ignore unknown parameter:%s\n",
291b3d94504SStephan Aßmus dp->parameters[i].name);
292b3d94504SStephan Aßmus }
293b3d94504SStephan Aßmus } /*for(...) enumerate "vendor" parameters*/
294b3d94504SStephan Aßmus } /*if(vendor_id == id){*/
295b3d94504SStephan Aßmus } /* if(dp && ... etc */
296b3d94504SStephan Aßmus return b_found;
297b3d94504SStephan Aßmus }
298b3d94504SStephan Aßmus /**
299b3d94504SStephan Aßmus \fn:lookup_device_settings
300b3d94504SStephan Aßmus \param vendor_id: vendor id of device to be checked for private settings
301b3d94504SStephan Aßmus \param product_id: product id of device to be checked for private settings
302b3d94504SStephan Aßmus \param udd: on return contains name,protocol etc. information about device
303b3d94504SStephan Aßmus \return: "true" if private settings for device found - "false" otherwise
304b3d94504SStephan Aßmus looks through driver settings file for private device description and load it
305b3d94504SStephan Aßmus if available into struct pointed by udd
306b3d94504SStephan Aßmus */
lookup_device_settings(const usb_device_descriptor * udd,usb_device_settings * uds)307b3d94504SStephan Aßmus bool lookup_device_settings(const usb_device_descriptor *udd,
308b3d94504SStephan Aßmus usb_device_settings *uds)
309b3d94504SStephan Aßmus {
310b3d94504SStephan Aßmus bool b_found = false;
311b3d94504SStephan Aßmus if(uds){
312b3d94504SStephan Aßmus void *sh = load_driver_settings(MODULE_NAME);
313b3d94504SStephan Aßmus if(sh){
314b3d94504SStephan Aßmus const driver_settings *ds = get_driver_settings(sh);
315b3d94504SStephan Aßmus if(ds){
316b3d94504SStephan Aßmus int i = 0;
317b3d94504SStephan Aßmus for(i = 0; i < ds->parameter_count; i++){
318b3d94504SStephan Aßmus if(SK_EQUAL(ds->parameters[i].name, skkVendor)){
319b3d94504SStephan Aßmus b_found = lookup_vendor_info(udd->vendor_id,
320b3d94504SStephan Aßmus udd->product_id,
321b3d94504SStephan Aßmus &ds->parameters[i], uds);
322b3d94504SStephan Aßmus if(b_found){
323b3d94504SStephan Aßmus uds->vendor_id = udd->vendor_id;
324b3d94504SStephan Aßmus break; //we've got it - stop enumeration.
325b3d94504SStephan Aßmus }
326b3d94504SStephan Aßmus }
327b3d94504SStephan Aßmus } /*for(...) - enumerate "root" parameters*/
328b3d94504SStephan Aßmus } /* if(ds) */
329b3d94504SStephan Aßmus unload_driver_settings(sh);
330b3d94504SStephan Aßmus } /* if(sh) */
331b3d94504SStephan Aßmus if(b_found){
332b3d94504SStephan Aßmus //TRACE("settings:loaded settings:'%s(%04x)/%s(%04x)/%08x'\n",
333b3d94504SStephan Aßmus TRACE("settings:loaded settings:'%04x/%04x/%08x'\n",
334b3d94504SStephan Aßmus /*descr->vendor_name,*/ uds->vendor_id,
335b3d94504SStephan Aßmus /*descr->product_name,*/
336b3d94504SStephan Aßmus uds->product_id, uds->properties);
337b3d94504SStephan Aßmus }
338b3d94504SStephan Aßmus } /* if(descr)*/
339b3d94504SStephan Aßmus return b_found;
340b3d94504SStephan Aßmus }
3412db0303fSimker
342