Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /*- |
2 | * Copyright (c) 1997, 1998, 1999 Nicolas Souchu | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * | |
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
24 | * SUCH DAMAGE. | |
25 | * | |
26 | * $FreeBSD: src/sys/dev/ppbus/vpo.c,v 1.20.2.1 2000/05/07 21:08:18 n_hibma Exp $ | |
27 | */ | |
28 | ||
29 | #include <sys/param.h> | |
30 | #include <sys/systm.h> | |
31 | #include <sys/module.h> | |
32 | #include <sys/bus.h> | |
33 | #include <sys/malloc.h> | |
d9ed64be | 34 | #include <sys/thread2.h> /* for crit_*() */ |
984263bc MD |
35 | |
36 | #include <machine/clock.h> | |
37 | ||
1f2de5d4 MD |
38 | #include <bus/cam/cam.h> |
39 | #include <bus/cam/cam_ccb.h> | |
40 | #include <bus/cam/cam_sim.h> | |
cec957e9 | 41 | #include <bus/cam/cam_xpt.h> |
1f2de5d4 | 42 | #include <bus/cam/cam_xpt_sim.h> |
cec957e9 | 43 | #include <bus/cam/cam_xpt_periph.h> |
1f2de5d4 MD |
44 | #include <bus/cam/cam_debug.h> |
45 | #include <bus/cam/cam_periph.h> | |
984263bc | 46 | |
cec957e9 | 47 | |
1f2de5d4 MD |
48 | #include <bus/cam/scsi/scsi_all.h> |
49 | #include <bus/cam/scsi/scsi_message.h> | |
50 | #include <bus/cam/scsi/scsi_da.h> | |
984263bc MD |
51 | |
52 | #include <sys/kernel.h> | |
53 | ||
54 | #include "opt_vpo.h" | |
55 | ||
1f2de5d4 MD |
56 | #include <bus/ppbus/ppbconf.h> |
57 | #include "vpoio.h" | |
984263bc MD |
58 | |
59 | #include "ppbus_if.h" | |
60 | ||
61 | struct vpo_sense { | |
62 | struct scsi_sense cmd; | |
63 | unsigned int stat; | |
64 | unsigned int count; | |
65 | }; | |
66 | ||
67 | struct vpo_data { | |
68 | unsigned short vpo_unit; | |
69 | ||
70 | int vpo_stat; | |
71 | int vpo_count; | |
72 | int vpo_error; | |
73 | ||
74 | int vpo_isplus; | |
75 | ||
76 | struct cam_sim *sim; | |
77 | ||
78 | struct vpo_sense vpo_sense; | |
79 | ||
80 | struct vpoio_data vpo_io; /* interface to low level functions */ | |
81 | }; | |
82 | ||
83 | #define DEVTOSOFTC(dev) \ | |
84 | ((struct vpo_data *)device_get_softc(dev)) | |
85 | ||
86 | /* cam related functions */ | |
87 | static void vpo_action(struct cam_sim *sim, union ccb *ccb); | |
88 | static void vpo_poll(struct cam_sim *sim); | |
89 | static void vpo_cam_rescan_callback(struct cam_periph *periph, | |
90 | union ccb *ccb); | |
91 | static void vpo_cam_rescan(struct vpo_data *vpo); | |
92 | ||
984263bc MD |
93 | /* |
94 | * vpo_probe() | |
95 | */ | |
96 | static int | |
97 | vpo_probe(device_t dev) | |
98 | { | |
99 | struct vpo_data *vpo; | |
100 | int error; | |
101 | ||
102 | vpo = DEVTOSOFTC(dev); | |
103 | bzero(vpo, sizeof(struct vpo_data)); | |
104 | ||
105 | /* vpo dependent initialisation */ | |
106 | vpo->vpo_unit = device_get_unit(dev); | |
107 | ||
108 | /* low level probe */ | |
109 | vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit); | |
110 | ||
111 | /* check ZIP before ZIP+ or imm_probe() will send controls to | |
112 | * the printer or whatelse connected to the port */ | |
113 | if ((error = vpoio_probe(dev, &vpo->vpo_io)) == 0) { | |
114 | vpo->vpo_isplus = 0; | |
115 | device_set_desc(dev, | |
116 | "Iomega VPI0 Parallel to SCSI interface"); | |
117 | } else if ((error = imm_probe(dev, &vpo->vpo_io)) == 0) { | |
118 | vpo->vpo_isplus = 1; | |
119 | device_set_desc(dev, | |
120 | "Iomega Matchmaker Parallel to SCSI interface"); | |
121 | } else { | |
122 | return (error); | |
123 | } | |
124 | ||
125 | return (0); | |
126 | } | |
127 | ||
128 | /* | |
129 | * vpo_attach() | |
130 | */ | |
131 | static int | |
132 | vpo_attach(device_t dev) | |
133 | { | |
134 | struct vpo_data *vpo = DEVTOSOFTC(dev); | |
135 | struct cam_devq *devq; | |
136 | int error; | |
137 | ||
138 | /* low level attachment */ | |
139 | if (vpo->vpo_isplus) { | |
140 | if ((error = imm_attach(&vpo->vpo_io))) | |
141 | return (error); | |
142 | } else { | |
143 | if ((error = vpoio_attach(&vpo->vpo_io))) | |
144 | return (error); | |
145 | } | |
146 | ||
147 | /* | |
148 | ** Now tell the generic SCSI layer | |
149 | ** about our bus. | |
150 | */ | |
151 | devq = cam_simq_alloc(/*maxopenings*/1); | |
152 | /* XXX What about low-level detach on error? */ | |
153 | if (devq == NULL) | |
154 | return (ENXIO); | |
155 | ||
156 | vpo->sim = cam_sim_alloc(vpo_action, vpo_poll, "vpo", vpo, | |
1c8b7a9a | 157 | device_get_unit(dev), &sim_mplock, |
984263bc | 158 | /*untagged*/1, /*tagged*/0, devq); |
3aed1355 | 159 | cam_simq_release(devq); |
984263bc | 160 | if (vpo->sim == NULL) { |
984263bc MD |
161 | return (ENXIO); |
162 | } | |
163 | ||
164 | if (xpt_bus_register(vpo->sim, /*bus*/0) != CAM_SUCCESS) { | |
3aed1355 | 165 | cam_sim_free(vpo->sim); |
984263bc MD |
166 | return (ENXIO); |
167 | } | |
168 | ||
169 | /* all went ok */ | |
170 | ||
171 | vpo_cam_rescan(vpo); /* have CAM rescan the bus */ | |
172 | ||
173 | return (0); | |
174 | } | |
175 | ||
176 | static void | |
177 | vpo_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) | |
178 | { | |
cec957e9 | 179 | xpt_free_ccb(&ccb->ccb_h); |
984263bc MD |
180 | } |
181 | ||
182 | static void | |
183 | vpo_cam_rescan(struct vpo_data *vpo) | |
184 | { | |
185 | struct cam_path *path; | |
cec957e9 | 186 | union ccb *ccb; |
984263bc | 187 | |
cec957e9 | 188 | ccb = xpt_alloc_ccb(); |
984263bc MD |
189 | if (xpt_create_path(&path, xpt_periph, cam_sim_path(vpo->sim), 0, 0) |
190 | != CAM_REQ_CMP) { | |
191 | /* A failure is benign as the user can do a manual rescan */ | |
192 | return; | |
193 | } | |
194 | ||
195 | xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); | |
196 | ccb->ccb_h.func_code = XPT_SCAN_BUS; | |
197 | ccb->ccb_h.cbfcnp = vpo_cam_rescan_callback; | |
198 | ccb->crcn.flags = CAM_FLAG_NONE; | |
199 | xpt_action(ccb); | |
200 | ||
201 | /* The scan is in progress now. */ | |
202 | } | |
203 | ||
204 | /* | |
205 | * vpo_intr() | |
206 | */ | |
207 | static void | |
208 | vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) | |
209 | { | |
71f385dc | 210 | int error; |
984263bc MD |
211 | #ifdef VP0_DEBUG |
212 | int i; | |
213 | #endif | |
214 | ||
d9ed64be | 215 | crit_enter(); |
984263bc MD |
216 | |
217 | if (vpo->vpo_isplus) { | |
71f385dc | 218 | error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, |
984263bc MD |
219 | csio->ccb_h.target_id, |
220 | (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, | |
221 | (char *)csio->data_ptr, csio->dxfer_len, | |
222 | &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); | |
223 | } else { | |
71f385dc | 224 | error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, |
984263bc MD |
225 | csio->ccb_h.target_id, |
226 | (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, | |
227 | (char *)csio->data_ptr, csio->dxfer_len, | |
228 | &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); | |
229 | } | |
230 | ||
231 | #ifdef VP0_DEBUG | |
e3869ec7 | 232 | kprintf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", |
71f385dc | 233 | error, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error); |
984263bc MD |
234 | |
235 | /* dump of command */ | |
236 | for (i=0; i<csio->cdb_len; i++) | |
e3869ec7 | 237 | kprintf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]); |
984263bc | 238 | |
e3869ec7 | 239 | kprintf("\n"); |
984263bc MD |
240 | #endif |
241 | ||
71f385dc | 242 | if (error) { |
984263bc MD |
243 | /* connection to ppbus interrupted */ |
244 | csio->ccb_h.status = CAM_CMD_TIMEOUT; | |
245 | goto error; | |
246 | } | |
247 | ||
248 | /* if a timeout occured, no sense */ | |
249 | if (vpo->vpo_error) { | |
250 | if (vpo->vpo_error != VP0_ESELECT_TIMEOUT) | |
e3869ec7 | 251 | kprintf("vpo%d: VP0 error/timeout (%d)\n", |
984263bc MD |
252 | vpo->vpo_unit, vpo->vpo_error); |
253 | ||
254 | csio->ccb_h.status = CAM_CMD_TIMEOUT; | |
255 | goto error; | |
256 | } | |
257 | ||
258 | /* check scsi status */ | |
259 | if (vpo->vpo_stat != SCSI_STATUS_OK) { | |
260 | csio->scsi_status = vpo->vpo_stat; | |
261 | ||
262 | /* check if we have to sense the drive */ | |
263 | if ((vpo->vpo_stat & SCSI_STATUS_CHECK_COND) != 0) { | |
264 | ||
265 | vpo->vpo_sense.cmd.opcode = REQUEST_SENSE; | |
266 | vpo->vpo_sense.cmd.length = csio->sense_len; | |
267 | vpo->vpo_sense.cmd.control = 0; | |
268 | ||
269 | if (vpo->vpo_isplus) { | |
71f385dc | 270 | error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, |
984263bc MD |
271 | csio->ccb_h.target_id, |
272 | (char *)&vpo->vpo_sense.cmd, | |
273 | sizeof(vpo->vpo_sense.cmd), | |
274 | (char *)&csio->sense_data, csio->sense_len, | |
275 | &vpo->vpo_sense.stat, &vpo->vpo_sense.count, | |
276 | &vpo->vpo_error); | |
277 | } else { | |
71f385dc | 278 | error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, |
984263bc MD |
279 | csio->ccb_h.target_id, |
280 | (char *)&vpo->vpo_sense.cmd, | |
281 | sizeof(vpo->vpo_sense.cmd), | |
282 | (char *)&csio->sense_data, csio->sense_len, | |
283 | &vpo->vpo_sense.stat, &vpo->vpo_sense.count, | |
284 | &vpo->vpo_error); | |
285 | } | |
286 | ||
287 | ||
288 | #ifdef VP0_DEBUG | |
e3869ec7 | 289 | kprintf("(sense) vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", |
71f385dc | 290 | error, vpo->vpo_sense.stat, vpo->vpo_sense.count, vpo->vpo_error); |
984263bc MD |
291 | #endif |
292 | ||
293 | /* check sense return status */ | |
71f385dc | 294 | if (error == 0 && vpo->vpo_sense.stat == SCSI_STATUS_OK) { |
984263bc MD |
295 | /* sense ok */ |
296 | csio->ccb_h.status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR; | |
297 | csio->sense_resid = csio->sense_len - vpo->vpo_sense.count; | |
298 | ||
299 | #ifdef VP0_DEBUG | |
300 | /* dump of sense info */ | |
e3869ec7 | 301 | kprintf("(sense) "); |
984263bc | 302 | for (i=0; i<vpo->vpo_sense.count; i++) |
e3869ec7 SW |
303 | kprintf("%x ", ((char *)&csio->sense_data)[i]); |
304 | kprintf("\n"); | |
984263bc MD |
305 | #endif |
306 | ||
307 | } else { | |
308 | /* sense failed */ | |
309 | csio->ccb_h.status = CAM_AUTOSENSE_FAIL; | |
310 | } | |
311 | } else { | |
312 | /* no sense */ | |
313 | csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; | |
314 | } | |
315 | ||
316 | goto error; | |
317 | } | |
318 | ||
319 | csio->resid = csio->dxfer_len - vpo->vpo_count; | |
320 | csio->ccb_h.status = CAM_REQ_CMP; | |
321 | ||
322 | error: | |
d9ed64be | 323 | crit_exit(); |
984263bc MD |
324 | |
325 | return; | |
326 | } | |
327 | ||
328 | static void | |
329 | vpo_action(struct cam_sim *sim, union ccb *ccb) | |
330 | { | |
331 | ||
332 | struct vpo_data *vpo = (struct vpo_data *)sim->softc; | |
333 | ||
334 | switch (ccb->ccb_h.func_code) { | |
335 | case XPT_SCSI_IO: | |
336 | { | |
337 | struct ccb_scsiio *csio; | |
338 | ||
339 | csio = &ccb->csio; | |
340 | ||
341 | #ifdef VP0_DEBUG | |
e3869ec7 | 342 | kprintf("vpo%d: XPT_SCSI_IO (0x%x) request\n", |
984263bc MD |
343 | vpo->vpo_unit, csio->cdb_io.cdb_bytes[0]); |
344 | #endif | |
345 | ||
346 | vpo_intr(vpo, csio); | |
347 | ||
348 | xpt_done(ccb); | |
349 | ||
350 | break; | |
351 | } | |
352 | case XPT_CALC_GEOMETRY: | |
353 | { | |
354 | struct ccb_calc_geometry *ccg; | |
355 | ||
356 | ccg = &ccb->ccg; | |
357 | ||
358 | #ifdef VP0_DEBUG | |
00449ae2 | 359 | kprintf("vpo%d: XPT_CALC_GEOMETRY (bs=%d,vs=%ju,c=%d,h=%d,spt=%d) request\n", |
984263bc MD |
360 | vpo->vpo_unit, |
361 | ccg->block_size, | |
00449ae2 | 362 | (uintmax_t)ccg->volume_size, |
984263bc MD |
363 | ccg->cylinders, |
364 | ccg->heads, | |
365 | ccg->secs_per_track); | |
366 | #endif | |
367 | ||
368 | ccg->heads = 64; | |
369 | ccg->secs_per_track = 32; | |
370 | ccg->cylinders = ccg->volume_size / | |
371 | (ccg->heads * ccg->secs_per_track); | |
372 | ||
373 | ccb->ccb_h.status = CAM_REQ_CMP; | |
374 | xpt_done(ccb); | |
375 | break; | |
376 | } | |
377 | case XPT_RESET_BUS: /* Reset the specified SCSI bus */ | |
378 | { | |
379 | ||
380 | #ifdef VP0_DEBUG | |
e3869ec7 | 381 | kprintf("vpo%d: XPT_RESET_BUS request\n", vpo->vpo_unit); |
984263bc MD |
382 | #endif |
383 | ||
384 | if (vpo->vpo_isplus) { | |
385 | if (imm_reset_bus(&vpo->vpo_io)) { | |
386 | ccb->ccb_h.status = CAM_REQ_CMP_ERR; | |
387 | xpt_done(ccb); | |
388 | return; | |
389 | } | |
390 | } else { | |
391 | if (vpoio_reset_bus(&vpo->vpo_io)) { | |
392 | ccb->ccb_h.status = CAM_REQ_CMP_ERR; | |
393 | xpt_done(ccb); | |
394 | return; | |
395 | } | |
396 | } | |
397 | ||
398 | ccb->ccb_h.status = CAM_REQ_CMP; | |
399 | xpt_done(ccb); | |
400 | break; | |
401 | } | |
402 | case XPT_PATH_INQ: /* Path routing inquiry */ | |
403 | { | |
404 | struct ccb_pathinq *cpi = &ccb->cpi; | |
405 | ||
406 | #ifdef VP0_DEBUG | |
e3869ec7 | 407 | kprintf("vpo%d: XPT_PATH_INQ request\n", vpo->vpo_unit); |
984263bc MD |
408 | #endif |
409 | cpi->version_num = 1; /* XXX??? */ | |
410 | cpi->hba_inquiry = 0; | |
411 | cpi->target_sprt = 0; | |
412 | cpi->hba_misc = 0; | |
413 | cpi->hba_eng_cnt = 0; | |
414 | cpi->max_target = 7; | |
415 | cpi->max_lun = 0; | |
416 | cpi->initiator_id = VP0_INITIATOR; | |
417 | cpi->bus_id = sim->bus_id; | |
418 | cpi->base_transfer_speed = 93; | |
419 | strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); | |
420 | strncpy(cpi->hba_vid, "Iomega", HBA_IDLEN); | |
421 | strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); | |
422 | cpi->unit_number = sim->unit_number; | |
423 | ||
424 | cpi->ccb_h.status = CAM_REQ_CMP; | |
425 | xpt_done(ccb); | |
426 | break; | |
427 | } | |
428 | default: | |
429 | ccb->ccb_h.status = CAM_REQ_INVALID; | |
430 | xpt_done(ccb); | |
431 | break; | |
432 | } | |
433 | ||
434 | return; | |
435 | } | |
436 | ||
437 | static void | |
438 | vpo_poll(struct cam_sim *sim) | |
439 | { | |
440 | /* The ZIP is actually always polled throw vpo_action() */ | |
441 | return; | |
442 | } | |
443 | ||
39b5d600 MD |
444 | /* |
445 | * Always create a "vpo" device under ppbus. Use device_identify to | |
446 | * create the static entry for any attached ppbus. | |
447 | */ | |
984263bc MD |
448 | static devclass_t vpo_devclass; |
449 | ||
450 | static device_method_t vpo_methods[] = { | |
451 | /* device interface */ | |
39b5d600 | 452 | DEVMETHOD(device_identify, bus_generic_identify), |
984263bc MD |
453 | DEVMETHOD(device_probe, vpo_probe), |
454 | DEVMETHOD(device_attach, vpo_attach), | |
d3c9c58e | 455 | DEVMETHOD_END |
984263bc MD |
456 | }; |
457 | ||
458 | static driver_t vpo_driver = { | |
459 | "vpo", | |
460 | vpo_methods, | |
461 | sizeof(struct vpo_data), | |
462 | }; | |
aa2b9d05 | 463 | DRIVER_MODULE(vpo, ppbus, vpo_driver, vpo_devclass, NULL, NULL); |
39b5d600 | 464 |