xref: /haiku/src/system/kernel/disk_device_manager/KDiskDevice.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
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 
30 KDiskDevice::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 
45 KDiskDevice::~KDiskDevice()
46 {
47 	Unset();
48 }
49 
50 
51 status_t
52 KDiskDevice::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 
93 void
94 KDiskDevice::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 
111 status_t
112 KDiskDevice::InitCheck() const
113 {
114 	return B_OK;
115 }
116 
117 
118 bool
119 KDiskDevice::ReadLock()
120 {
121 	return rw_lock_read_lock(&fLocker) == B_OK;
122 }
123 
124 
125 void
126 KDiskDevice::ReadUnlock()
127 {
128 	rw_lock_read_unlock(&fLocker);
129 }
130 
131 
132 bool
133 KDiskDevice::WriteLock()
134 {
135 	return rw_lock_write_lock(&fLocker) == B_OK;
136 }
137 
138 
139 void
140 KDiskDevice::WriteUnlock()
141 {
142 	rw_lock_write_unlock(&fLocker);
143 }
144 
145 
146 void
147 KDiskDevice::SetID(partition_id id)
148 {
149 	KPartition::SetID(id);
150 	fDeviceData.id = id;
151 }
152 
153 
154 status_t
155 KDiskDevice::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 
163 status_t
164 KDiskDevice::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 
172 status_t
173 KDiskDevice::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 
181 void
182 KDiskDevice::SetDeviceFlags(uint32 flags)
183 {
184 	fDeviceData.flags = flags;
185 }
186 
187 
188 uint32
189 KDiskDevice::DeviceFlags() const
190 {
191 	return fDeviceData.flags;
192 }
193 
194 
195 bool
196 KDiskDevice::IsReadOnlyMedia() const
197 {
198 	return fDeviceData.geometry.read_only;
199 }
200 
201 
202 bool
203 KDiskDevice::IsWriteOnce() const
204 {
205 	return fDeviceData.geometry.write_once;
206 }
207 
208 
209 bool
210 KDiskDevice::IsRemovable() const
211 {
212 	return fDeviceData.geometry.removable;
213 }
214 
215 
216 bool
217 KDiskDevice::HasMedia() const
218 {
219 	return fMediaStatus == B_OK || fMediaStatus == B_DEV_MEDIA_CHANGED;
220 }
221 
222 
223 bool
224 KDiskDevice::MediaChanged() const
225 {
226 	return fMediaStatus == B_DEV_MEDIA_CHANGED;
227 }
228 
229 
230 void
231 KDiskDevice::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 
239 void
240 KDiskDevice::UninitializeMedia()
241 {
242 	UninitializeContents();
243 	_ResetGeometry();
244 	_UpdateDeviceFlags();
245 	_InitPartitionData();
246 }
247 
248 
249 void
250 KDiskDevice::UpdateGeometry()
251 {
252 	if (GetGeometry(&fDeviceData.geometry) != B_OK)
253 		return;
254 
255 	_UpdateDeviceFlags();
256 	_InitPartitionData();
257 }
258 
259 
260 status_t
261 KDiskDevice::SetPath(const char* path)
262 {
263 	return set_string(fDeviceData.path, path);
264 }
265 
266 
267 const char*
268 KDiskDevice::Path() const
269 {
270 	return fDeviceData.path;
271 }
272 
273 
274 status_t
275 KDiskDevice::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 
283 status_t
284 KDiskDevice::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 
294 void
295 KDiskDevice::SetFD(int fd)
296 {
297 	fFD = fd;
298 }
299 
300 
301 int
302 KDiskDevice::FD() const
303 {
304 	return fFD;
305 }
306 
307 
308 disk_device_data*
309 KDiskDevice::DeviceData()
310 {
311 	return &fDeviceData;
312 }
313 
314 
315 const disk_device_data*
316 KDiskDevice::DeviceData() const
317 {
318 	return &fDeviceData;
319 }
320 
321 
322 void
323 KDiskDevice::WriteUserData(UserDataWriter& writer, user_partition_data* data)
324 {
325 	return KPartition::WriteUserData(writer, data);
326 }
327 
328 
329 void
330 KDiskDevice::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 
346 void
347 KDiskDevice::Dump(bool deep, int32 level)
348 {
349 	OUT("device %ld: %s\n", ID(), Path());
350 	OUT("  media status:      %s\n", strerror(fMediaStatus));
351 	OUT("  device flags:      %lx\n", DeviceFlags());
352 	if (fMediaStatus == B_OK)
353 		KPartition::Dump(deep, 0);
354 }
355 
356 
357 status_t
358 KDiskDevice::GetMediaStatus(status_t* mediaStatus)
359 {
360 	status_t error = B_OK;
361 	if (ioctl(fFD, B_GET_MEDIA_STATUS, 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 
380 status_t
381 KDiskDevice::GetGeometry(device_geometry* geometry)
382 {
383 	if (ioctl(fFD, B_GET_GEOMETRY, geometry) != 0)
384 		return errno;
385 	return B_OK;
386 }
387 
388 
389 void
390 KDiskDevice::_InitPartitionData()
391 {
392 	fDeviceData.id = fPartitionData.id;
393 	fPartitionData.block_size = fDeviceData.geometry.bytes_per_sector;
394 	fPartitionData.offset = 0;
395 	fPartitionData.size = (off_t)fPartitionData.block_size
396 		* fDeviceData.geometry.sectors_per_track
397 		* fDeviceData.geometry.cylinder_count
398 		* fDeviceData.geometry.head_count;
399 	fPartitionData.flags |= B_PARTITION_IS_DEVICE;
400 
401 	char name[B_FILE_NAME_LENGTH];
402 	if (ioctl(fFD, B_GET_DEVICE_NAME, name, sizeof(name)) == B_OK)
403 		fPartitionData.name = strdup(name);
404 }
405 
406 
407 void
408 KDiskDevice::_ResetGeometry()
409 {
410 	fDeviceData.geometry.bytes_per_sector = 0;
411 	fDeviceData.geometry.sectors_per_track = 0;
412 	fDeviceData.geometry.cylinder_count = 0;
413 	fDeviceData.geometry.head_count = 0;
414 	fDeviceData.geometry.device_type = B_DISK;
415 	fDeviceData.geometry.removable = true;
416 	fDeviceData.geometry.read_only = true;
417 	fDeviceData.geometry.write_once = false;
418 }
419 
420 
421 void
422 KDiskDevice::_UpdateDeviceFlags()
423 {
424 	if (fDeviceData.geometry.removable)
425 		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_REMOVABLE);
426 	if (HasMedia())
427 		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_HAS_MEDIA);
428 	else
429 		SetDeviceFlags(DeviceFlags() & ~B_DISK_DEVICE_HAS_MEDIA);
430 
431 	if (fDeviceData.geometry.read_only)
432 		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_READ_ONLY);
433 	if (fDeviceData.geometry.write_once)
434 		SetDeviceFlags(DeviceFlags() | B_DISK_DEVICE_WRITE_ONCE);
435 }
436