1/*
2 * Copyright 2006-2011, Axel D��rfler, axeld@pinc-software.de.
3 * Copyright 2003-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "KDiskDevice.h"
9
10#include <errno.h>
11#include <fcntl.h>
12#include <stdio.h>
13#include <unistd.h>
14
15#include <KernelExport.h>
16#include <Drivers.h>
17
18#include "ddm_userland_interface.h"
19#include "KDiskDeviceUtils.h"
20#include "KPath.h"
21#include "UserDataWriter.h"
22
23
24// debugging
25//#define DBG(x)
26#define DBG(x) x
27#define OUT dprintf
28
29
30KDiskDevice::KDiskDevice(partition_id id)
31	:
32	KPartition(id),
33	fDeviceData(),
34	fFD(-1),
35	fMediaStatus(B_ERROR)
36{
37	rw_lock_init(&fLocker, "disk device");
38
39	Unset();
40	fDevice = this;
41	fPublishedName = (char*)"raw";
42}
43
44
45KDiskDevice::~KDiskDevice()
46{
47	Unset();
48}
49
50
51status_t
52KDiskDevice::SetTo(const char* path)
53{
54	// check initialization and parameter
55	status_t error = InitCheck();
56	if (error != B_OK)
57		return error;
58	if (!path)
59		return B_BAD_VALUE;
60	Unset();
61	// set the path
62	error = set_string(fDeviceData.path, path);
63	if (error != B_OK)
64		return error;
65	// open the device
66	fFD = open(path, O_RDONLY);
67	if (fFD < 0)
68		return errno;
69	// get media status
70	error = GetMediaStatus(&fMediaStatus);
71	if (error != B_OK)
72		return error;
73	if (fMediaStatus == B_DEV_MEDIA_CHANGED)
74		fMediaStatus = B_OK;
75	// get device geometry
76	if (fMediaStatus == B_OK) {
77		error = GetGeometry(&fDeviceData.geometry);
78		if (error != B_OK)
79			return error;
80	} else {
81		// no media present: reset the geometry
82		_ResetGeometry();
83	}
84
85	// set device flags
86	_UpdateDeviceFlags();
87	// update partition data
88	_InitPartitionData();
89	return B_OK;
90}
91
92
93void
94KDiskDevice::Unset()
95{
96	if (fFD >= 0) {
97		close(fFD);
98		fFD = -1;
99	}
100	fMediaStatus = B_ERROR;
101	fDeviceData.id = -1;
102	fDeviceData.flags = 0;
103	if (fDeviceData.path) {
104		free(fDeviceData.path);
105		fDeviceData.path = NULL;
106	}
107	_ResetGeometry();
108}
109
110
111status_t
112KDiskDevice::InitCheck() const
113{
114	return B_OK;
115}
116
117
118bool
119KDiskDevice::ReadLock()
120{
121	return rw_lock_read_lock(&fLocker) == B_OK;
122}
123
124
125void
126KDiskDevice::ReadUnlock()
127{
128	rw_lock_read_unlock(&fLocker);
129}
130
131
132bool
133KDiskDevice::WriteLock()
134{
135	return rw_lock_write_lock(&fLocker) == B_OK;
136}
137
138
139void
140KDiskDevice::WriteUnlock()
141{
142	rw_lock_write_unlock(&fLocker);
143}
144
145
146void
147KDiskDevice::SetID(partition_id id)
148{
149	KPartition::SetID(id);
150	fDeviceData.id = id;
151}
152
153
154status_t
155KDiskDevice::PublishDevice()
156{
157	// PublishDevice(), UnpublishDevice() and Republish are no-ops
158	// for KDiskDevices, since they are always published.
159	return B_OK;
160}
161
162
163status_t
164KDiskDevice::UnpublishDevice()
165{
166	// PublishDevice(), UnpublishDevice() and Republish are no-ops
167	// for KDiskDevices, since they are always published.
168	return B_OK;
169}
170
171
172status_t
173KDiskDevice::RepublishDevice()
174{
175	// PublishDevice(), UnpublishDevice() and Republish are no-ops
176	// for KDiskDevices, since they are always published.
177	return B_OK;
178}
179
180
181void
182KDiskDevice::SetDeviceFlags(uint32 flags)
183{
184	fDeviceData.flags = flags;
185}
186
187
188uint32
189KDiskDevice::DeviceFlags() const
190{
191	return fDeviceData.flags;
192}
193
194
195bool
196KDiskDevice::IsReadOnlyMedia() const
197{
198	return fDeviceData.geometry.read_only;
199}
200
201
202bool
203KDiskDevice::IsWriteOnce() const
204{
205	return fDeviceData.geometry.write_once;
206}
207
208
209bool
210KDiskDevice::IsRemovable() const
211{
212	return fDeviceData.geometry.removable;
213}
214
215
216bool
217KDiskDevice::HasMedia() const
218{
219	return fMediaStatus == B_OK || fMediaStatus == B_DEV_MEDIA_CHANGED;
220}
221
222
223bool
224KDiskDevice::MediaChanged() const
225{
226	return fMediaStatus == B_DEV_MEDIA_CHANGED;
227}
228
229
230void
231KDiskDevice::UpdateMediaStatusIfNeeded()
232{
233	// TODO: allow a device to notify us about its media status!
234	// This will then also need to clear any B_DEV_MEDIA_CHANGED
235	GetMediaStatus(&fMediaStatus);
236}
237
238
239void
240KDiskDevice::UninitializeMedia()
241{
242	UninitializeContents();
243	_ResetGeometry();
244	_UpdateDeviceFlags();
245	_InitPartitionData();
246}
247
248
249void
250KDiskDevice::UpdateGeometry()
251{
252	if (GetGeometry(&fDeviceData.geometry) != B_OK)
253		return;
254
255	_UpdateDeviceFlags();
256	_InitPartitionData();
257}
258
259
260status_t
261KDiskDevice::SetPath(const char* path)
262{
263	return set_string(fDeviceData.path, path);
264}
265
266
267const char*
268KDiskDevice::Path() const
269{
270	return fDeviceData.path;
271}
272
273
274status_t
275KDiskDevice::GetFileName(char* buffer, size_t size) const
276{
277	if (strlcpy(buffer, "raw", size) >= size)
278		return B_NAME_TOO_LONG;
279	return B_OK;
280}
281
282
283status_t
284KDiskDevice::GetPath(KPath* path) const
285{
286	if (!path || path->InitCheck() != B_OK)
287		return B_BAD_VALUE;
288	if (!fDeviceData.path)
289		return B_NO_INIT;
290	return path->SetPath(fDeviceData.path);
291}
292
293
294void
295KDiskDevice::SetFD(int fd)
296{
297	fFD = fd;
298}
299
300
301int
302KDiskDevice::FD() const
303{
304	return fFD;
305}
306
307
308disk_device_data*
309KDiskDevice::DeviceData()
310{
311	return &fDeviceData;
312}
313
314
315const disk_device_data*
316KDiskDevice::DeviceData() const
317{
318	return &fDeviceData;
319}
320
321
322void
323KDiskDevice::WriteUserData(UserDataWriter& writer, user_partition_data* data)
324{
325	KPartition::WriteUserData(writer, data);
326}
327
328
329void
330KDiskDevice::WriteUserData(UserDataWriter& writer)
331{
332	KPartition* partition = this;
333	user_disk_device_data* data
334		= writer.AllocateDeviceData(partition->CountChildren());
335	char* path = writer.PlaceString(Path());
336	if (data != NULL) {
337		data->device_flags = DeviceFlags();
338		data->path = path;
339		writer.AddRelocationEntry(&data->path);
340		partition->WriteUserData(writer, &data->device_partition_data);
341	} else
342		partition->WriteUserData(writer, NULL);
343}
344
345
346void
347KDiskDevice::Dump(bool deep, int32 level)
348{
349	OUT("device %" B_PRId32 ": %s\n", ID(), Path());
350	OUT("  media status:      %s\n", strerror(fMediaStatus));
351	OUT("  device flags:      %" B_PRIx32 "\n", DeviceFlags());
352	if (fMediaStatus == B_OK)
353		KPartition::Dump(deep, 0);
354}
355
356
357status_t
358KDiskDevice::GetMediaStatus(status_t* mediaStatus)
359{
360	status_t error = B_OK;
361	if (ioctl(fFD, B_GET_MEDIA_STATUS, mediaStatus, sizeof(*mediaStatus)) != 0)
362		error = errno;
363	// maybe the device driver doesn't implement this ioctl -- see, if getting
364	// the device geometry succeeds
365	if (error != B_OK) {
366		device_geometry geometry;
367		if (GetGeometry(&geometry) == B_OK) {
368			// if the device is not removable, we can ignore the failed ioctl
369			// and return a media status of B_OK
370			if (!geometry.removable) {
371				error = B_OK;
372				*mediaStatus = B_OK;
373			}
374		}
375	}
376	return error;
377}
378
379
380status_t
381KDiskDevice::GetGeometry(device_geometry* geometry)
382{
383	if (ioctl(fFD, B_GET_GEOMETRY, geometry, sizeof(*geometry)) != 0)
384		return errno;
385	return B_OK;
386}
387
388
389void
390KDiskDevice::_InitPartitionData()
391{
392	fDeviceData.id = fPartitionData.id;
393	fPartitionData.block_size = fDeviceData.geometry.bytes_per_sector;
394	fPartitionData.physical_block_size = fDeviceData.geometry.bytes_per_physical_sector;
395	fPartitionData.offset = 0;
396	fPartitionData.size = (off_t)fPartitionData.block_size
397		* fDeviceData.geometry.sectors_per_track
398		* fDeviceData.geometry.cylinder_count
399		* fDeviceData.geometry.head_count;
400	fPartitionData.flags |= B_PARTITION_IS_DEVICE;
401
402	char name[B_FILE_NAME_LENGTH];
403	if (ioctl(fFD, B_GET_DEVICE_NAME, name, sizeof(name)) == B_OK)
404		fPartitionData.name = strdup(name);
405}
406
407
408void
409KDiskDevice::_ResetGeometry()
410{
411	fDeviceData.geometry.bytes_per_sector = 0;
412	fDeviceData.geometry.sectors_per_track = 0;
413	fDeviceData.geometry.cylinder_count = 0;
414	fDeviceData.geometry.head_count = 0;
415	fDeviceData.geometry.device_type = B_DISK;
416	fDeviceData.geometry.removable = true;
417	fDeviceData.geometry.read_only = true;
418	fDeviceData.geometry.write_once = false;
419}
420
421
422void
423KDiskDevice::_UpdateDeviceFlags()
424{
425	if (fDeviceData.geometry.removable)
426		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_REMOVABLE);
427	if (HasMedia())
428		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_HAS_MEDIA);
429	else
430		SetDeviceFlags(DeviceFlags() & ~B_DISK_DEVICE_HAS_MEDIA);
431
432	if (fDeviceData.geometry.read_only)
433		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_READ_ONLY);
434	if (fDeviceData.geometry.write_once)
435		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_WRITE_ONCE);
436}
437