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
22 device_manager_info *pnp;
23
24
25 static status_t
raw_open(void * device_cookie,uint32 flags,void ** handle_cookie)26 raw_open(void *device_cookie, uint32 flags, void **handle_cookie)
27 {
28 *handle_cookie = device_cookie;
29 return B_OK;
30 }
31
32
33 static status_t
raw_close(void * cookie)34 raw_close(void *cookie)
35 {
36 return B_OK;
37 }
38
39
40 static status_t
raw_free(void * cookie)41 raw_free(void *cookie)
42 {
43 return B_OK;
44 }
45
46
47 #if 0
48 static status_t
49 raw_control(void *cookie, uint32 op, void *data, size_t len)
50 {
51 return B_OK;
52 }
53 #endif
54
55
56 static status_t
raw_read(void * cookie,off_t position,void * data,size_t * numBytes)57 raw_read(void *cookie, off_t position, void *data, size_t *numBytes)
58 {
59 return B_ERROR;
60 }
61
62
63 static status_t
raw_write(void * cookie,off_t position,const void * data,size_t * numBytes)64 raw_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 */
72 static status_t
raw_command(raw_device_info * device,raw_device_command * cmd)73 raw_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
127 static status_t
raw_ioctl(raw_device_info * device,int op,void * buffer,size_t length)128 raw_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
147 static status_t
raw_init_device(device_node_handle node,void * user_cookie,void ** cookie)148 raw_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
172 err:
173 free(device);
174 return res;
175 }
176
177
178 static status_t
raw_uninit_device(raw_device_info * device)179 raw_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
192 static status_t
raw_device_added(device_node_handle node)193 raw_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
234 static status_t
std_ops(int32 op,...)235 std_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
248 module_dependency module_dependencies[] = {
249 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp },
250 {}
251 };
252
253 pnp_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
283 module_info *modules[] = {
284 (module_info *)&scsi_raw_module,
285 NULL
286 };
287