Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
script3r
GitHub Repository: script3r/os161
Path: blob/master/kern/dev/lamebus/lhd.c
2111 views
1
/*
2
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
3
* The President and Fellows of Harvard College.
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
* 3. Neither the name of the University nor the names of its contributors
14
* may be used to endorse or promote products derived from this software
15
* without specific prior written permission.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
/*
31
* LAMEbus hard disk (lhd) driver.
32
*/
33
34
#include <types.h>
35
#include <kern/errno.h>
36
#include <lib.h>
37
#include <uio.h>
38
#include <synch.h>
39
#include <platform/bus.h>
40
#include <vfs.h>
41
#include <lamebus/lhd.h>
42
#include "autoconf.h"
43
44
/* Registers (offsets within slot) */
45
#define LHD_REG_NSECT 0 /* Number of sectors */
46
#define LHD_REG_STAT 4 /* Status */
47
#define LHD_REG_SECT 8 /* Sector for I/O */
48
#define LHD_REG_RPM 12 /* Disk rotation speed (revs per minute) */
49
50
/* Status codes */
51
#define LHD_IDLE 0 /* Device idle */
52
#define LHD_WORKING 1 /* Operation in progress */
53
#define LHD_OK 4 /* Operation succeeded */
54
#define LHD_INVSECT 12 /* Invalid sector requested */
55
#define LHD_MEDIA 20 /* Media error */
56
#define LHD_ISWRITE 2 /* OR with above: I/O is a write */
57
#define LHD_STATEMASK 0x1d /* mask for masking out LHD_ISWRITE */
58
59
/* Buffer (offset within slot) */
60
#define LHD_BUFFER 32768
61
62
/*
63
* Shortcut for reading a register.
64
*/
65
static
66
inline
67
uint32_t lhd_rdreg(struct lhd_softc *lh, uint32_t reg)
68
{
69
return bus_read_register(lh->lh_busdata, lh->lh_buspos, reg);
70
}
71
72
/*
73
* Shortcut for writing a register.
74
*/
75
static
76
inline
77
void lhd_wreg(struct lhd_softc *lh, uint32_t reg, uint32_t val)
78
{
79
bus_write_register(lh->lh_busdata, lh->lh_buspos, reg, val);
80
}
81
82
/*
83
* Convert a result code from the hardware to an errno value.
84
*/
85
static
86
int lhd_code_to_errno(struct lhd_softc *lh, int code)
87
{
88
switch (code & LHD_STATEMASK) {
89
case LHD_OK: return 0;
90
case LHD_INVSECT: return EINVAL;
91
case LHD_MEDIA: return EIO;
92
}
93
kprintf("lhd%d: Unknown result code %d\n", lh->lh_unit, code);
94
return EAGAIN;
95
}
96
97
/*
98
* Record that an I/O has completed: save the result and poke the
99
* completion semaphore.
100
*/
101
static
102
void
103
lhd_iodone(struct lhd_softc *lh, int err)
104
{
105
lh->lh_result = err;
106
V(lh->lh_done);
107
}
108
109
/*
110
* Interrupt handler for lhd.
111
* Read the status register; if an operation finished, clear the status
112
* register and report completion.
113
*/
114
void
115
lhd_irq(void *vlh)
116
{
117
struct lhd_softc *lh = vlh;
118
uint32_t val;
119
120
val = lhd_rdreg(lh, LHD_REG_STAT);
121
122
switch (val & LHD_STATEMASK) {
123
case LHD_IDLE:
124
case LHD_WORKING:
125
break;
126
case LHD_OK:
127
case LHD_INVSECT:
128
case LHD_MEDIA:
129
lhd_wreg(lh, LHD_REG_STAT, 0);
130
lhd_iodone(lh, lhd_code_to_errno(lh, val));
131
break;
132
}
133
}
134
135
/*
136
* Function called when we are open()'d.
137
*/
138
static
139
int
140
lhd_open(struct device *d, int openflags)
141
{
142
/*
143
* Don't need to do anything.
144
*/
145
(void)d;
146
(void)openflags;
147
148
return 0;
149
}
150
151
/*
152
* Function called when we are close()'d.
153
*/
154
static
155
int
156
lhd_close(struct device *d)
157
{
158
/*
159
* Don't need to do anything.
160
*/
161
(void)d;
162
163
return 0;
164
}
165
166
/*
167
* Function for handling ioctls.
168
*/
169
static
170
int
171
lhd_ioctl(struct device *d, int op, userptr_t data)
172
{
173
/*
174
* We don't support any ioctls.
175
*/
176
(void)d;
177
(void)op;
178
(void)data;
179
return EIOCTL;
180
}
181
182
#if 0
183
/*
184
* Reset the device.
185
* This could be used, for instance, on timeout, if you implement suitable
186
* facilities.
187
*/
188
static
189
void
190
lhd_reset(struct lhd_softc *lh)
191
{
192
lhd_wreg(lh, LHD_REG_STAT, 0);
193
}
194
#endif
195
196
/*
197
* I/O function (for both reads and writes)
198
*/
199
static
200
int
201
lhd_io(struct device *d, struct uio *uio)
202
{
203
struct lhd_softc *lh = d->d_data;
204
205
uint32_t sector = uio->uio_offset / LHD_SECTSIZE;
206
uint32_t sectoff = uio->uio_offset % LHD_SECTSIZE;
207
uint32_t len = uio->uio_resid / LHD_SECTSIZE;
208
uint32_t lenoff = uio->uio_resid % LHD_SECTSIZE;
209
uint32_t i;
210
uint32_t statval = LHD_WORKING;
211
int result;
212
213
/* Don't allow I/O that isn't sector-aligned. */
214
if (sectoff != 0 || lenoff != 0) {
215
return EINVAL;
216
}
217
218
/* Don't allow I/O past the end of the disk. */
219
if (sector+len > lh->lh_dev.d_blocks) {
220
return EINVAL;
221
}
222
223
/* Set up the value to write into the status register. */
224
if (uio->uio_rw==UIO_WRITE) {
225
statval |= LHD_ISWRITE;
226
}
227
228
/* Loop over all the sectors we were asked to do. */
229
for (i=0; i<len; i++) {
230
231
/* Wait until nobody else is using the device. */
232
P(lh->lh_clear);
233
234
/*
235
* Are we writing? If so, transfer the data to the
236
* on-card buffer.
237
*/
238
if (uio->uio_rw == UIO_WRITE) {
239
result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
240
if (result) {
241
V(lh->lh_clear);
242
return result;
243
}
244
}
245
246
/* Tell it what sector we want... */
247
lhd_wreg(lh, LHD_REG_SECT, sector+i);
248
249
/* and start the operation. */
250
lhd_wreg(lh, LHD_REG_STAT, statval);
251
252
/* Now wait until the interrupt handler tells us we're done. */
253
P(lh->lh_done);
254
255
/* Get the result value saved by the interrupt handler. */
256
result = lh->lh_result;
257
258
/*
259
* Are we reading? If so, and if we succeeded,
260
* transfer the data out of the on-card buffer.
261
*/
262
if (result==0 && uio->uio_rw==UIO_READ) {
263
result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
264
}
265
266
/* Tell another thread it's cleared to go ahead. */
267
V(lh->lh_clear);
268
269
/* If we failed, return the error. */
270
if (result) {
271
return result;
272
}
273
}
274
275
return 0;
276
}
277
278
/*
279
* Setup routine called by autoconf.c when an lhd is found.
280
*/
281
int
282
config_lhd(struct lhd_softc *lh, int lhdno)
283
{
284
char name[32];
285
286
/* Figure out what our name is. */
287
snprintf(name, sizeof(name), "lhd%d", lhdno);
288
289
/* Get a pointer to the on-chip buffer. */
290
lh->lh_buf = bus_map_area(lh->lh_busdata, lh->lh_buspos, LHD_BUFFER);
291
292
/* Create the semaphores. */
293
lh->lh_clear = sem_create("lhd-clear", 1);
294
if (lh->lh_clear == NULL) {
295
return ENOMEM;
296
}
297
lh->lh_done = sem_create("lhd-done", 0);
298
if (lh->lh_done == NULL) {
299
sem_destroy(lh->lh_clear);
300
lh->lh_clear = NULL;
301
return ENOMEM;
302
}
303
304
/* Set up the VFS device structure. */
305
lh->lh_dev.d_open = lhd_open;
306
lh->lh_dev.d_close = lhd_close;
307
lh->lh_dev.d_io = lhd_io;
308
lh->lh_dev.d_ioctl = lhd_ioctl;
309
lh->lh_dev.d_blocks = bus_read_register(lh->lh_busdata, lh->lh_buspos,
310
LHD_REG_NSECT);
311
lh->lh_dev.d_blocksize = LHD_SECTSIZE;
312
lh->lh_dev.d_data = lh;
313
314
/* Add the VFS device structure to the VFS device list. */
315
return vfs_adddev(name, &lh->lh_dev, 1);
316
}
317
318