1/*
2 * Copyright 2002/03, Thomas Kurschel. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6/*
7	Part of Open SCSI raw driver.
8
9	We don't support particular file handles, instead we use
10	file handle = device handle.
11*/
12
13
14#include "scsi_raw.h"
15#include <string.h>
16#include <scsi.h>
17#include <malloc.h>
18#include <pnp_devfs.h>
19#include <stdio.h>
20
21
22device_manager_info *pnp;
23
24
25static status_t
26raw_open(void *device_cookie, uint32 flags, void **handle_cookie)
27{
28	*handle_cookie = device_cookie;
29	return B_OK;
30}
31
32
33static status_t
34raw_close(void *cookie)
35{
36	return B_OK;
37}
38
39
40static status_t
41raw_free(void *cookie)
42{
43	return B_OK;
44}
45
46
47#if 0
48static status_t
49raw_control(void *cookie, uint32 op, void *data, size_t len)
50{
51	return B_OK;
52}
53#endif
54
55
56static status_t
57raw_read(void *cookie, off_t position, void *data, size_t *numBytes)
58{
59	return B_ERROR;
60}
61
62
63static status_t
64raw_write(void *cookie, off_t position, const void *data, size_t *numBytes)
65{
66	return B_ERROR;
67}
68
69
70/* TODO: sync with scsi_periph module, this has been updated there to use
71	user_memcpy */
72static status_t
73raw_command(raw_device_info *device, raw_device_command *cmd)
74{
75	scsi_ccb *request;
76
77	SHOW_FLOW0(3, "");
78
79	request = device->scsi->alloc_ccb(device->scsi_device);
80	if (request == NULL)
81		return B_NO_MEMORY;
82
83	request->flags = 0;
84
85	if (cmd->flags & B_RAW_DEVICE_DATA_IN)
86		request->flags |= SCSI_DIR_IN;
87	else if (cmd->data_length)
88		request->flags |= SCSI_DIR_OUT;
89	else
90		request->flags |= SCSI_DIR_NONE;
91
92	request->data = cmd->data;
93	request->sg_list = NULL;
94	request->data_len = cmd->data_length;
95	request->sort = -1;
96	request->timeout = cmd->timeout;
97
98	memcpy(request->cdb, cmd->command, SCSI_MAX_CDB_SIZE);
99	request->cdb_len = cmd->command_length;
100
101	device->scsi->sync_io(request);
102
103	// TBD: should we call standard error handler here, or may the
104	// actions done there (like starting the unit) confuse the application?
105
106	cmd->cam_status = request->subsys_status;
107	cmd->scsi_status = request->device_status;
108
109	if ((request->subsys_status & SCSI_AUTOSNS_VALID) != 0 && cmd->sense_data) {
110		memcpy(cmd->sense_data, request->sense,
111			min((int32)cmd->sense_data_length, SCSI_MAX_SENSE_SIZE - request->sense_resid));
112	}
113
114	if ((cmd->flags & B_RAW_DEVICE_REPORT_RESIDUAL) != 0) {
115		// this is a bit strange, see Be's sample code where I pinched this from;
116		// normally, residual means "number of unused bytes left"
117		// but here, we have to return "number of used bytes", which is the opposite
118		cmd->data_length = cmd->data_length - request->data_resid;
119		cmd->sense_data_length = SCSI_MAX_SENSE_SIZE - request->sense_resid;
120	}
121
122	device->scsi->free_ccb(request);
123	return B_OK;
124}
125
126
127static status_t
128raw_ioctl(raw_device_info *device, int op, void *buffer, size_t length)
129{
130	status_t res;
131
132	switch (op) {
133		case B_RAW_DEVICE_COMMAND:
134			res = raw_command(device, buffer);
135			break;
136
137		default:
138			res = B_DEV_INVALID_IOCTL;
139	}
140
141	SHOW_FLOW(4, "%x: %s", op, strerror(res));
142
143	return res;
144}
145
146
147static status_t
148raw_init_device(device_node_handle node, void *user_cookie, void **cookie)
149{
150	raw_device_info *device;
151	status_t res;
152
153	SHOW_FLOW0(3, "");
154
155	device = (raw_device_info *)calloc(1, sizeof(*device));
156	if (device == NULL)
157		return B_NO_MEMORY;
158
159	device->node = node;
160
161	// register it everywhere
162	res = pnp->init_driver(pnp->get_parent(node), NULL,
163			(driver_module_info **)&device->scsi, (void **)&device->scsi_device);
164	if (res != B_OK)
165		goto err;
166
167	SHOW_FLOW0(3, "done");
168
169	*cookie = device;
170	return B_OK;
171
172err:
173	free(device);
174	return res;
175}
176
177
178static status_t
179raw_uninit_device(raw_device_info *device)
180{
181	pnp->uninit_driver(pnp->get_parent(device->node));
182	free(device);
183
184	return B_OK;
185}
186
187
188/**	called whenever a new SCSI device was added to system;
189 *	we register a devfs entry for every device
190 */
191
192static status_t
193raw_device_added(device_node_handle node)
194{
195	uint8 path_id, target_id, target_lun;
196	char name[100];
197
198	SHOW_FLOW0(3, "");
199
200	// compose name
201	if (pnp->get_attr_uint8(node, SCSI_BUS_PATH_ID_ITEM, &path_id, true) != B_OK
202		|| pnp->get_attr_uint8(node, SCSI_DEVICE_TARGET_ID_ITEM, &target_id, true) != B_OK
203		|| pnp->get_attr_uint8(node, SCSI_DEVICE_TARGET_LUN_ITEM, &target_lun, true) != B_OK)
204		return B_ERROR;
205
206	sprintf(name, "bus/scsi/%d/%d/%d/raw",
207		path_id, target_id, target_lun);
208
209	SHOW_FLOW(3, "name=%s", name);
210
211	// ready to register
212	{
213		device_attr attrs[] = {
214			{ B_DRIVER_MODULE, B_STRING_TYPE, { .string = SCSI_RAW_MODULE_NAME }},
215
216			// default connection is used by peripheral drivers, and as we don't
217			// want to kick them out, we use concurrent "raw" connection
218			// (btw: this shows nicely that something goes wrong: one device
219			// and two drivers means begging for trouble)
220			{ PNP_DRIVER_CONNECTION, B_STRING_TYPE, { .string = "raw" }},
221
222			// we want devfs on top of us (who wouldn't?)
223			{ B_DRIVER_FIXED_CHILD, B_STRING_TYPE, { .string = PNP_DEVFS_MODULE_NAME }},
224			// tell which name we want to have in devfs
225			{ PNP_DEVFS_FILENAME, B_STRING_TYPE, { .string = name }},
226			{ NULL }
227		};
228
229		return pnp->register_device(node, attrs, NULL, &node);
230	}
231}
232
233
234static status_t
235std_ops(int32 op, ...)
236{
237	switch (op) {
238		case B_MODULE_INIT:
239		case B_MODULE_UNINIT:
240			return B_OK;
241
242		default:
243			return B_ERROR;
244	}
245}
246
247
248module_dependency module_dependencies[] = {
249	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp },
250	{}
251};
252
253pnp_devfs_driver_info scsi_raw_module = {
254	{
255		{
256			SCSI_RAW_MODULE_NAME,
257			0,
258			std_ops
259		},
260
261		NULL,
262		raw_device_added,
263		raw_init_device,
264		(status_t (*) (void *))raw_uninit_device,
265		NULL
266	},
267
268	(status_t (*)(void *, uint32, void **)) 		&raw_open,
269	raw_close,
270	raw_free,
271	(status_t (*)(void *, uint32, void *, size_t))	&raw_ioctl,
272
273	raw_read,
274	raw_write,
275
276	NULL,
277	NULL,
278
279	NULL,
280	NULL
281};
282
283module_info *modules[] = {
284	(module_info *)&scsi_raw_module,
285	NULL
286};
287