1/*
2 * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include <string.h>
7
8#include <KernelExport.h>
9#include <Drivers.h>
10
11#include <lock.h>
12#include <util/AutoLock.h>
13#include <util/kernel_cpp.h>
14
15#include "RemoteDisk.h"
16
17
18//#define TRACE_REMOTE_DISK
19#ifdef TRACE_REMOTE_DISK
20#	define TRACE(x) dprintf x
21#else
22#	define TRACE(x) do {} while (false)
23#endif
24
25
26const bigtime_t kInitRetryDelay	= 10 * 1000000LL;	// 10 s
27
28enum {
29	MAX_REMOTE_DISKS	= 1
30};
31
32
33struct RemoteDiskDevice : recursive_lock {
34	RemoteDisk*		remoteDisk;
35	bigtime_t		lastInitRetryTime;
36
37	RemoteDiskDevice()
38		:
39		remoteDisk(NULL),
40		lastInitRetryTime(-1)
41	{
42	}
43
44	~RemoteDiskDevice()
45	{
46		delete remoteDisk;
47		Uninit();
48	}
49
50	status_t Init()
51	{
52		recursive_lock_init(this, "remote disk device");
53		return B_OK;
54	}
55
56	void Uninit()
57	{
58		recursive_lock_destroy(this);
59	}
60
61	status_t LazyInitDisk()
62	{
63		if (remoteDisk)
64			return B_OK;
65
66		// don't try to init, if the last attempt wasn't long enough ago
67		if (lastInitRetryTime >= 0
68			&& system_time() < lastInitRetryTime + kInitRetryDelay) {
69			return B_ERROR;
70		}
71
72		// create the object
73		remoteDisk = new(nothrow) RemoteDisk;
74		if (!remoteDisk) {
75			lastInitRetryTime = system_time();
76			return B_NO_MEMORY;
77		}
78
79		// find a server
80		TRACE(("remote_disk: FindAnyRemoteDisk()\n"));
81		status_t error = remoteDisk->FindAnyRemoteDisk();
82		if (error != B_OK) {
83			delete remoteDisk;
84			remoteDisk = NULL;
85			lastInitRetryTime = system_time();
86			return B_NO_MEMORY;
87		}
88
89		return B_OK;
90	}
91
92	void GetGeometry(device_geometry* geometry, bool bios)
93	{
94		// TODO: Respect "bios" argument!
95		geometry->bytes_per_sector = REMOTE_DISK_BLOCK_SIZE;
96		geometry->sectors_per_track = 1;
97		geometry->cylinder_count = remoteDisk->Size() / REMOTE_DISK_BLOCK_SIZE;
98		geometry->head_count = 1;
99		geometry->device_type = B_DISK;
100		geometry->removable = true;
101		geometry->read_only = remoteDisk->IsReadOnly();
102		geometry->write_once = false;
103		geometry->bytes_per_physical_sector = REMOTE_DISK_BLOCK_SIZE;
104	}
105};
106
107typedef RecursiveLocker DeviceLocker;
108
109
110static const char* kPublishedNames[] = {
111	"disk/virtual/remote_disk/0/raw",
112//	"misc/remote_disk_control",
113	NULL
114};
115
116static RemoteDiskDevice* sDevices;
117
118
119// #pragma mark - internal functions
120
121
122// device_for_name
123static RemoteDiskDevice*
124device_for_name(const char* name)
125{
126	for (int i = 0; i < MAX_REMOTE_DISKS; i++) {
127		if (strcmp(name, kPublishedNames[i]) == 0)
128			return sDevices + i;
129	}
130	return NULL;
131}
132
133
134// #pragma mark - data device hooks
135
136
137static status_t
138remote_disk_open(const char* name, uint32 flags, void** cookie)
139{
140	RemoteDiskDevice* device = device_for_name(name);
141	TRACE(("remote_disk_open(\"%s\") -> %p\n", name, device));
142	if (!device)
143		return B_BAD_VALUE;
144
145	DeviceLocker locker(device);
146	status_t error = device->LazyInitDisk();
147	if (error != B_OK)
148		return error;
149
150	*cookie = device;
151
152	return B_OK;
153}
154
155
156static status_t
157remote_disk_close(void* cookie)
158{
159	TRACE(("remote_disk_close(%p)\n", cookie));
160
161	// nothing to do
162	return B_OK;
163}
164
165
166static status_t
167remote_disk_read(void* cookie, off_t position, void* buffer, size_t* numBytes)
168{
169	TRACE(("remote_disk_read(%p, %lld, %p, %lu)\n", cookie, position, buffer,
170		*numBytes));
171
172	RemoteDiskDevice* device = (RemoteDiskDevice*)cookie;
173	DeviceLocker locker(device);
174
175	ssize_t bytesRead = device->remoteDisk->ReadAt(position, buffer, *numBytes);
176	if (bytesRead < 0) {
177		*numBytes = 0;
178		TRACE(("remote_disk_read() failed: %s\n", strerror(bytesRead)));
179		return bytesRead;
180	}
181
182	*numBytes = bytesRead;
183	TRACE(("remote_disk_read() done: %ld\n", bytesRead));
184	return B_OK;
185}
186
187
188static status_t
189remote_disk_write(void* cookie, off_t position, const void* buffer,
190	size_t* numBytes)
191{
192	TRACE(("remote_disk_write(%p, %lld, %p, %lu)\n", cookie, position, buffer,
193		*numBytes));
194
195	RemoteDiskDevice* device = (RemoteDiskDevice*)cookie;
196	DeviceLocker locker(device);
197
198	ssize_t bytesWritten = device->remoteDisk->WriteAt(position, buffer,
199		*numBytes);
200	if (bytesWritten < 0) {
201		*numBytes = 0;
202		TRACE(("remote_disk_write() failed: %s\n", strerror(bytesRead)));
203		return bytesWritten;
204	}
205
206	*numBytes = bytesWritten;
207	TRACE(("remote_disk_written() done: %ld\n", bytesWritten));
208	return B_OK;
209}
210
211
212static status_t
213remote_disk_control(void* cookie, uint32 op, void* arg, size_t len)
214{
215	TRACE(("remote_disk_control(%p, %lu, %p, %lu)\n", cookie, op, arg, len));
216
217	RemoteDiskDevice* device = (RemoteDiskDevice*)cookie;
218	DeviceLocker locker(device);
219
220	// used data device
221	switch (op) {
222		case B_GET_DEVICE_SIZE:
223			TRACE(("remote_disk: B_GET_DEVICE_SIZE\n"));
224			*(size_t*)arg = device->remoteDisk->Size();
225			return B_OK;
226
227		case B_SET_NONBLOCKING_IO:
228			TRACE(("remote_disk: B_SET_NONBLOCKING_IO\n"));
229			return B_OK;
230
231		case B_SET_BLOCKING_IO:
232			TRACE(("remote_disk: B_SET_BLOCKING_IO\n"));
233			return B_OK;
234
235		case B_GET_READ_STATUS:
236			TRACE(("remote_disk: B_GET_READ_STATUS\n"));
237			*(bool*)arg = true;
238			return B_OK;
239
240		case B_GET_WRITE_STATUS:
241			TRACE(("remote_disk: B_GET_WRITE_STATUS\n"));
242			*(bool*)arg = true;
243			return B_OK;
244
245		case B_GET_ICON:
246		{
247			TRACE(("remote_disk: B_GET_ICON\n"));
248			return B_BAD_VALUE;
249		}
250
251		case B_GET_BIOS_GEOMETRY:
252		case B_GET_GEOMETRY:
253		{
254			TRACE(("remote_disk: %s\n",
255				op == B_GET_BIOS_GEOMETRY ? "B_GET_BIOS_GEOMETRY" : "B_GET_GEOMETRY"));
256			if (arg == NULL || len > sizeof(device_geometry))
257				return B_BAD_VALUE;
258
259			device_geometry geometry;
260			device->GetGeometry(&geometry, op == B_GET_BIOS_GEOMETRY);
261			return user_memcpy(arg, &geometry, len);
262		}
263
264		case B_GET_MEDIA_STATUS:
265			TRACE(("remote_disk: B_GET_MEDIA_STATUS\n"));
266			*(status_t*)arg = B_NO_ERROR;
267			return B_OK;
268
269		case B_SET_UNINTERRUPTABLE_IO:
270			TRACE(("remote_disk: B_SET_UNINTERRUPTABLE_IO\n"));
271			return B_OK;
272
273		case B_SET_INTERRUPTABLE_IO:
274			TRACE(("remote_disk: B_SET_INTERRUPTABLE_IO\n"));
275			return B_OK;
276
277		case B_FLUSH_DRIVE_CACHE:
278			TRACE(("remote_disk: B_FLUSH_DRIVE_CACHE\n"));
279			return B_OK;
280
281		case B_GET_BIOS_DRIVE_ID:
282			TRACE(("remote_disk: B_GET_BIOS_DRIVE_ID\n"));
283			*(uint8*)arg = 0xF8;
284			return B_OK;
285
286		case B_GET_DRIVER_FOR_DEVICE:
287		case B_SET_DEVICE_SIZE:
288		case B_SET_PARTITION:
289		case B_FORMAT_DEVICE:
290		case B_EJECT_DEVICE:
291		case B_LOAD_MEDIA:
292		case B_GET_NEXT_OPEN_DEVICE:
293			TRACE(("remote_disk: another ioctl: %lx (%lu)\n", op, op));
294			return B_BAD_VALUE;
295
296		default:
297			TRACE(("remote_disk: unknown ioctl: %lx (%lu)\n", op, op));
298			return B_BAD_VALUE;
299	}
300}
301
302
303static status_t
304remote_disk_free(void* cookie)
305{
306	TRACE(("remote_disk_free(%p)\n", cookie));
307
308	// nothing to do
309	return B_OK;
310}
311
312
313static device_hooks sDataDeviceHooks = {
314	remote_disk_open,
315	remote_disk_close,
316	remote_disk_free,
317	remote_disk_control,
318	remote_disk_read,
319	remote_disk_write
320};
321
322
323// #pragma mark - public API
324
325
326int32 api_version = B_CUR_DRIVER_API_VERSION;
327
328
329status_t
330init_hardware(void)
331{
332	TRACE(("remote_disk: init_hardware()\n"));
333
334	return B_OK;
335}
336
337
338status_t
339init_driver(void)
340{
341	TRACE(("remote_disk: init_driver()\n"));
342
343	sDevices = new(nothrow) RemoteDiskDevice[MAX_REMOTE_DISKS];
344	if (!sDevices)
345		return B_NO_MEMORY;
346
347	status_t error = B_OK;
348	for (int i = 0; error == B_OK && i < MAX_REMOTE_DISKS; i++)
349		error = sDevices[i].Init();
350
351	if (error != B_OK) {
352		delete[] sDevices;
353		sDevices = NULL;
354		return error;
355	}
356
357	return B_OK;
358}
359
360
361void
362uninit_driver(void)
363{
364	TRACE(("remote_disk: uninit_driver()\n"));
365
366	delete[] sDevices;
367}
368
369
370const char**
371publish_devices(void)
372{
373	TRACE(("remote_disk: publish_devices()\n"));
374	return kPublishedNames;
375}
376
377
378device_hooks*
379find_device(const char* name)
380{
381	TRACE(("remote_disk: find_device(%s)\n", name));
382	return &sDataDeviceHooks;
383}
384
385