xref: /freebsd/sys/dev/ida/ida_disk.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*-
2  * Copyright (c) 1999,2000 Jonathan Lemon
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$
27  */
28 
29 /*
30  * Disk driver for Compaq SMART RAID adapters.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 
37 #include <sys/bio.h>
38 #include <sys/bus.h>
39 #include <sys/conf.h>
40 #include <sys/devicestat.h>
41 #include <sys/disk.h>
42 
43 #if NPCI > 0
44 #include <machine/bus_memio.h>
45 #endif
46 #include <machine/bus_pio.h>
47 #include <machine/bus.h>
48 #include <sys/rman.h>
49 
50 #include <dev/ida/idareg.h>
51 #include <dev/ida/idavar.h>
52 
53 /* prototypes */
54 static int idad_probe(device_t dev);
55 static int idad_attach(device_t dev);
56 static int idad_detach(device_t dev);
57 
58 static	d_open_t	idad_open;
59 static	d_close_t	idad_close;
60 static	d_strategy_t	idad_strategy;
61 
62 #define IDAD_BDEV_MAJOR	29
63 #define IDAD_CDEV_MAJOR	109
64 
65 static struct cdevsw id_cdevsw = {
66 	/* open */	idad_open,
67 	/* close */	idad_close,
68 	/* read */	physread,
69 	/* write */	physwrite,
70 	/* ioctl */	noioctl,
71 	/* poll */	nopoll,
72 	/* mmap */	nommap,
73 	/* strategy */	idad_strategy,
74 	/* name */ 	"idad",
75 	/* maj */	IDAD_CDEV_MAJOR,
76 	/* dump */	nodump,
77 	/* psize */ 	nopsize,
78 	/* flags */	D_DISK,
79 	/* bmaj */	IDAD_BDEV_MAJOR
80 };
81 
82 static devclass_t	idad_devclass;
83 static struct cdevsw 	idaddisk_cdevsw;
84 static int		disks_registered = 0;
85 
86 static device_method_t idad_methods[] = {
87 	DEVMETHOD(device_probe,		idad_probe),
88 	DEVMETHOD(device_attach,	idad_attach),
89 	DEVMETHOD(device_detach,	idad_detach),
90 	{ 0, 0 }
91 };
92 
93 static driver_t idad_driver = {
94 	"idad",
95 	idad_methods,
96 	sizeof(struct idad_softc)
97 };
98 
99 DRIVER_MODULE(idad, ida, idad_driver, idad_devclass, 0, 0);
100 
101 static __inline struct idad_softc *
102 idad_getsoftc(dev_t dev)
103 {
104 
105 	return ((struct idad_softc *)dev->si_drv1);
106 }
107 
108 static int
109 idad_open(dev_t dev, int flags, int fmt, struct proc *p)
110 {
111 	struct idad_softc *drv;
112 	struct disklabel *label;
113 
114 	drv = idad_getsoftc(dev);
115 	if (drv == NULL)
116 		return (ENXIO);
117 
118 	label = &drv->disk.d_label;
119 	bzero(label, sizeof(*label));
120 	label->d_type = DTYPE_SCSI;
121 	label->d_secsize = drv->secsize;
122 	label->d_nsectors = drv->sectors;
123 	label->d_ntracks = drv->heads;
124 	label->d_ncylinders = drv->cylinders;
125 	label->d_secpercyl = drv->sectors * drv->heads;
126 	label->d_secperunit = drv->secperunit;
127 
128 	return (0);
129 }
130 
131 static int
132 idad_close(dev_t dev, int flags, int fmt, struct proc *p)
133 {
134 	struct idad_softc *drv;
135 
136 	drv = idad_getsoftc(dev);
137 	if (drv == NULL)
138 		return (ENXIO);
139 	return (0);
140 }
141 
142 /*
143  * Read/write routine for a buffer.  Finds the proper unit, range checks
144  * arguments, and schedules the transfer.  Does not wait for the transfer
145  * to complete.  Multi-page transfers are supported.  All I/O requests must
146  * be a multiple of a sector in length.
147  */
148 static void
149 idad_strategy(struct bio *bp)
150 {
151 	struct idad_softc *drv;
152 	int s;
153 
154 	drv = idad_getsoftc(bp->bio_dev);
155 	if (drv == NULL) {
156     		bp->bio_error = EINVAL;
157 		goto bad;
158 	}
159 
160 	/*
161 	 * software write protect check
162 	 */
163 	if (drv->flags & DRV_WRITEPROT && (bp->bio_cmd == BIO_WRITE)) {
164 		bp->bio_error = EROFS;
165 		goto bad;
166 	}
167 
168 	/*
169 	 * If it's a null transfer, return immediately
170 	 */
171 	if (bp->bio_bcount == 0)
172 		goto done;
173 
174 	bp->bio_driver1 = drv;
175 	s = splbio();
176 	devstat_start_transaction(&drv->stats);
177 	ida_submit_buf(drv->controller, bp);
178 	splx(s);
179 	return;
180 
181 bad:
182 	bp->bio_flags |= BIO_ERROR;
183 
184 done:
185 	/*
186 	 * Correctly set the buf to indicate a completed transfer
187 	 */
188 	bp->bio_resid = bp->bio_bcount;
189 	biodone(bp);
190 	return;
191 }
192 
193 void
194 idad_intr(struct bio *bp)
195 {
196 	struct idad_softc *drv = (struct idad_softc *)bp->bio_driver1;
197 
198 	if (bp->bio_flags & BIO_ERROR)
199 		bp->bio_error = EIO;
200 	else
201 		bp->bio_resid = 0;
202 
203 	devstat_end_transaction_bio(&drv->stats, bp);
204 	biodone(bp);
205 }
206 
207 static int
208 idad_probe(device_t dev)
209 {
210 
211 	device_set_desc(dev, "Compaq Logical Drive");
212 	return (0);
213 }
214 
215 static int
216 idad_attach(device_t dev)
217 {
218 	struct ida_drive_info dinfo;
219 	struct idad_softc *drv;
220 	device_t parent;
221 	dev_t dsk;
222 	int error;
223 
224 	drv = (struct idad_softc *)device_get_softc(dev);
225 	parent = device_get_parent(dev);
226 	drv->controller = (struct ida_softc *)device_get_softc(parent);
227 	drv->unit = device_get_unit(dev);
228 	drv->drive = drv->controller->num_drives;
229 	drv->controller->num_drives++;
230 
231 	error = ida_command(drv->controller, CMD_GET_LOG_DRV_INFO,
232 	    &dinfo, sizeof(dinfo), drv->drive, DMA_DATA_IN);
233 	if (error) {
234 		device_printf(dev, "CMD_GET_LOG_DRV_INFO failed\n");
235 		return (ENXIO);
236 	}
237 
238 	drv->cylinders = dinfo.ncylinders;
239 	drv->heads = dinfo.nheads;
240 	drv->sectors = dinfo.nsectors;
241 	drv->secsize = dinfo.secsize == 0 ? 512 : dinfo.secsize;
242 	drv->secperunit = dinfo.secperunit;
243 
244 	/* XXX
245 	 * other initialization
246 	 */
247 	device_printf(dev, "%uMB (%u sectors), blocksize=%d\n",
248 	    drv->secperunit / ((1024 * 1024) / drv->secsize),
249 	    drv->secperunit, drv->secsize);
250 
251 	devstat_add_entry(&drv->stats, "idad", drv->unit, drv->secsize,
252 	    DEVSTAT_NO_ORDERED_TAGS,
253 	    DEVSTAT_TYPE_STORARRAY| DEVSTAT_TYPE_IF_OTHER,
254 	    DEVSTAT_PRIORITY_ARRAY);
255 
256 	dsk = disk_create(drv->unit, &drv->disk, 0,
257 	    &id_cdevsw, &idaddisk_cdevsw);
258 
259 	dsk->si_drv1 = drv;
260 	dsk->si_iosize_max = DFLTPHYS;		/* XXX guess? */
261 	disks_registered++;
262 
263 	return (0);
264 }
265 
266 static int
267 idad_detach(device_t dev)
268 {
269 	struct idad_softc *drv;
270 
271 	drv = (struct idad_softc *)device_get_softc(dev);
272 	devstat_remove_entry(&drv->stats);
273 
274 	if (--disks_registered == 0)
275 		cdevsw_remove(&idaddisk_cdevsw);
276 	return (0);
277 }
278