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