1 /* This file contains device independent device driver interface.
4 * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
5 * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
6 * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
7 * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
10 * The drivers support the following operations (using message format m2):
12 * m_type DEVICE IO_ENDPT COUNT POSITION ADRRESS
13 * ----------------------------------------------------------------
14 * | DEV_OPEN | device | proc nr | | | |
15 * |------------+---------+---------+---------+---------+---------|
16 * | DEV_CLOSE | device | proc nr | | | |
17 * |------------+---------+---------+---------+---------+---------|
18 * | DEV_READ | device | proc nr | bytes | offset | buf ptr |
19 * |------------+---------+---------+---------+---------+---------|
20 * | DEV_WRITE | device | proc nr | bytes | offset | buf ptr |
21 * |------------+---------+---------+---------+---------+---------|
22 * | DEV_GATHER | device | proc nr | iov len | offset | iov ptr |
23 * |------------+---------+---------+---------+---------+---------|
24 * | DEV_SCATTER| device | proc nr | iov len | offset | iov ptr |
25 * |------------+---------+---------+---------+---------+---------|
26 * | DEV_IOCTL | device | proc nr |func code| | buf ptr |
27 * |------------+---------+---------+---------+---------+---------|
28 * | CANCEL | device | proc nr | r/w | | |
29 * |------------+---------+---------+---------+---------+---------|
30 * | HARD_STOP | | | | | |
31 * |------------+---------+---------+---------+---------+---------|
32 * | DEV_*_S | variants using safecopies of above |
33 * ----------------------------------------------------------------
35 * The file contains one entry point:
37 * driver_task: called by the device dependent task entry
41 #include "../drivers.h"
42 #include <sys/ioc_disk.h>
48 #if USE_EXTRA_DMA_BUF && DMA_BUF_SIZE < 2048
49 /* A bit extra scratch for the Adaptec driver. */
50 #define BUF_EXTRA (2048 - DMA_BUF_SIZE)
55 /* Claim space for variables. */
56 PRIVATE u8_t buffer
[(unsigned) 2 * DMA_BUF_SIZE
+ BUF_EXTRA
];
57 u8_t
*tmp_buf
; /* the DMA buffer eventually */
58 phys_bytes tmp_phys
; /* phys address of DMA buffer */
60 #else /* CHIP != INTEL */
62 /* Claim space for variables. */
63 u8_t tmp_buf
[DMA_BUF_SIZE
]; /* the DMA buffer */
64 phys_bytes tmp_phys
; /* phys address of DMA buffer */
66 #endif /* CHIP != INTEL */
68 FORWARD
_PROTOTYPE( void init_buffer
, (void) );
69 FORWARD
_PROTOTYPE( int do_rdwt
, (struct driver
*dr
, message
*mp
, int safe
) );
70 FORWARD
_PROTOTYPE( int do_vrdwt
, (struct driver
*dr
, message
*mp
, int safe
) );
73 PRIVATE mq_t
*queue_head
= NULL
;
75 /*===========================================================================*
77 *===========================================================================*/
78 PUBLIC
void driver_task(dp
)
79 struct driver
*dp
; /* Device dependent entry points. */
81 /* Main program of any device driver task. */
86 /* Init MQ library. */
89 /* Get a DMA buffer. */
92 /* Here is the main loop of the disk task. It waits for a message, carries
93 * it out, and sends a reply.
96 /* Any queued messages? Oldest are at the head. */
100 memcpy(&mess
, &mq
->mq_mess
, sizeof(mess
));
101 queue_head
= queue_head
->mq_next
;
105 /* Wait for a request to read or write a disk block. */
106 if ((s
=receive(ANY
, &mess
)) != OK
)
107 panic((*dp
->dr_name
)(),"receive() failed", s
);
110 device_caller
= mess
.m_source
;
111 proc_nr
= mess
.IO_ENDPT
;
113 /* Now carry out the work. */
114 switch(mess
.m_type
) {
115 case DEV_OPEN
: r
= (*dp
->dr_open
)(dp
, &mess
); break;
116 case DEV_CLOSE
: r
= (*dp
->dr_close
)(dp
, &mess
); break;
118 case DEV_IOCTL
: r
= (*dp
->dr_ioctl
)(dp
, &mess
, 0); break;
120 case DEV_IOCTL_S
: r
= (*dp
->dr_ioctl
)(dp
, &mess
, 1); break;
121 case CANCEL
: r
= (*dp
->dr_cancel
)(dp
, &mess
);break;
122 case DEV_SELECT
: r
= (*dp
->dr_select
)(dp
, &mess
);break;
125 case DEV_WRITE
: r
= do_rdwt(dp
, &mess
, 0); break;
128 case DEV_WRITE_S
: r
= do_rdwt(dp
, &mess
, 1); break;
131 case DEV_SCATTER
: r
= do_vrdwt(dp
, &mess
, 0); break;
134 case DEV_SCATTER_S
: r
= do_vrdwt(dp
, &mess
, 1); break;
136 case HARD_INT
: /* leftover interrupt or expired timer. */
138 (*dp
->dr_hw_int
)(dp
, &mess
);
142 case SYS_SIG
: (*dp
->dr_signal
)(dp
, &mess
);
143 continue; /* don't reply */
144 case SYN_ALARM
: (*dp
->dr_alarm
)(dp
, &mess
);
145 continue; /* don't reply */
146 case DEV_PING
: notify(mess
.m_source
);
150 r
= (*dp
->dr_other
)(dp
, &mess
, 0);
156 /* Clean up leftover state. */
159 /* Finally, prepare and send the reply message. */
160 if (r
!= EDONTREPLY
) {
161 mess
.m_type
= TASK_REPLY
;
162 mess
.REP_ENDPT
= proc_nr
;
163 /* Status is # of bytes transferred or error code. */
165 send(device_caller
, &mess
);
171 /*===========================================================================*
173 *===========================================================================*/
174 PRIVATE
void init_buffer()
176 /* Select a buffer that can safely be used for DMA transfers. It may also
177 * be used to read partition tables and such. Its absolute address is
178 * 'tmp_phys', the normal address is 'tmp_buf'.
185 sys_umap(SELF
, D
, (vir_bytes
)buffer
, (phys_bytes
)sizeof(buffer
), &tmp_phys
);
187 if ((left
= dma_bytes_left(tmp_phys
)) < DMA_BUF_SIZE
) {
188 /* First half of buffer crosses a 64K boundary, can't DMA into that */
192 #endif /* CHIP == INTEL */
195 /*===========================================================================*
197 *===========================================================================*/
198 PRIVATE
int do_rdwt(dp
, mp
, safe
)
199 struct driver
*dp
; /* device dependent entry points */
200 message
*mp
; /* pointer to read or write message */
201 int safe
; /* use safecopies? */
203 /* Carry out a single read or write request. */
206 phys_bytes phys_addr
;
209 /* Disk address? Address and length of the user buffer? */
210 if (mp
->COUNT
< 0) return(EINVAL
);
212 /* Check the user buffer (not relevant for safe copies). */
214 sys_umap(mp
->IO_ENDPT
, D
, (vir_bytes
) mp
->ADDRESS
, mp
->COUNT
, &phys_addr
);
215 if (phys_addr
== 0) return(EFAULT
);
218 /* Prepare for I/O. */
219 if ((*dp
->dr_prepare
)(mp
->DEVICE
) == NIL_DEV
) return(ENXIO
);
221 /* Create a one element scatter/gather vector for the buffer. */
224 mp
->m_type
== DEV_READ
||
226 mp
->m_type
== DEV_READ_S
) opcode
= DEV_GATHER_S
;
227 else opcode
= DEV_SCATTER_S
;
229 iovec1
.iov_addr
= (vir_bytes
) mp
->ADDRESS
;
230 iovec1
.iov_size
= mp
->COUNT
;
232 /* Transfer bytes from/to the device. */
233 position
= make64(mp
->POSITION
, mp
->HIGHPOS
);
234 r
= (*dp
->dr_transfer
)(mp
->IO_ENDPT
, opcode
, position
, &iovec1
, 1, safe
);
236 /* Return the number of bytes transferred or an error code. */
237 return(r
== OK
? (mp
->COUNT
- iovec1
.iov_size
) : r
);
240 /*==========================================================================*
242 *==========================================================================*/
243 PRIVATE
int do_vrdwt(dp
, mp
, safe
)
244 struct driver
*dp
; /* device dependent entry points */
245 message
*mp
; /* pointer to read or write message */
246 int safe
; /* use safecopies? */
248 /* Carry out an device read or write to/from a vector of user addresses.
249 * The "user addresses" are assumed to be safe, i.e. FS transferring to/from
250 * its own buffers, so they are not checked.
252 static iovec_t iovec
[NR_IOREQS
];
254 phys_bytes iovec_size
;
259 nr_req
= mp
->COUNT
; /* Length of I/O vector */
262 /* Copy the vector from the caller to kernel space. */
263 if (nr_req
> NR_IOREQS
) nr_req
= NR_IOREQS
;
264 iovec_size
= (phys_bytes
) (nr_req
* sizeof(iovec
[0]));
267 if (OK
!= sys_safecopyfrom(mp
->m_source
, (vir_bytes
) mp
->IO_GRANT
,
268 0, (vir_bytes
) iovec
, iovec_size
, D
)) {
269 panic((*dp
->dr_name
)(),"bad (safe) I/O vector by", mp
->m_source
);
272 if (OK
!= sys_datacopy(mp
->m_source
, (vir_bytes
) mp
->ADDRESS
,
273 SELF
, (vir_bytes
) iovec
, iovec_size
)) {
274 panic((*dp
->dr_name
)(),"bad I/O vector by", mp
->m_source
);
281 /* Prepare for I/O. */
282 if ((*dp
->dr_prepare
)(mp
->DEVICE
) == NIL_DEV
) return(ENXIO
);
284 /* Transfer bytes from/to the device. */
286 position
= make64(mp
->POSITION
, mp
->HIGHPOS
);
287 r
= (*dp
->dr_transfer
)(mp
->IO_ENDPT
, opcode
, position
, iov
,
290 /* Copy the I/O vector back to the caller. */
292 if (OK
!= sys_safecopyto(mp
->m_source
, (vir_bytes
) mp
->IO_GRANT
,
293 0, (vir_bytes
) iovec
, iovec_size
, D
)) {
294 panic((*dp
->dr_name
)(),"couldn't return I/O vector", mp
->m_source
);
297 sys_datacopy(SELF
, (vir_bytes
) iovec
,
298 mp
->m_source
, (vir_bytes
) mp
->ADDRESS
, iovec_size
);
304 /*===========================================================================*
306 *===========================================================================*/
307 PUBLIC
char *no_name()
309 /* Use this default name if there is no specific name for the device. This was
310 * originally done by fetching the name from the task table for this process:
311 * "return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);", but currently a
312 * real "noname" is returned. Perhaps, some system information service can be
313 * queried for a name at a later time.
315 static char name
[] = "noname";
319 /*============================================================================*
321 *============================================================================*/
322 PUBLIC
int do_nop(dp
, mp
)
326 /* Nothing there, or nothing to do. */
328 switch (mp
->m_type
) {
329 case DEV_OPEN
: return(ENODEV
);
330 case DEV_CLOSE
: return(OK
);
333 case DEV_IOCTL
: return(ENOTTY
);
335 default: printf("nop: ignoring code %d\n", mp
->m_type
); return(EIO
);
339 /*============================================================================*
341 *============================================================================*/
342 PUBLIC
int nop_ioctl(dp
, mp
, safe
)
350 /*============================================================================*
352 *============================================================================*/
353 PUBLIC
void nop_signal(dp
, mp
)
357 /* Default action for signal is to ignore. */
360 /*============================================================================*
362 *============================================================================*/
363 PUBLIC
void nop_alarm(dp
, mp
)
367 /* Ignore the leftover alarm. */
370 /*===========================================================================*
372 *===========================================================================*/
373 PUBLIC
struct device
*nop_prepare(device
)
375 /* Nothing to prepare for. */
379 /*===========================================================================*
381 *===========================================================================*/
382 PUBLIC
void nop_cleanup()
384 /* Nothing to clean up. */
387 /*===========================================================================*
389 *===========================================================================*/
390 PUBLIC
int nop_cancel(struct driver
*dr
, message
*m
)
392 /* Nothing to do for cancel. */
396 /*===========================================================================*
398 *===========================================================================*/
399 PUBLIC
int nop_select(struct driver
*dr
, message
*m
)
401 /* Nothing to do for select. */
405 /*============================================================================*
407 *============================================================================*/
408 PUBLIC
int do_diocntl(dp
, mp
, safe
)
410 message
*mp
; /* pointer to ioctl request */
411 int safe
; /* addresses or grants? */
413 /* Carry out a partition setting/getting request. */
415 struct partition entry
;
418 if (mp
->REQUEST
!= DIOCSETP
&& mp
->REQUEST
!= DIOCGETP
) {
420 return dp
->dr_other(dp
, mp
, safe
);
421 } else return(ENOTTY
);
424 /* Decode the message parameters. */
425 if ((dv
= (*dp
->dr_prepare
)(mp
->DEVICE
)) == NIL_DEV
) return(ENXIO
);
427 if (mp
->REQUEST
== DIOCSETP
) {
428 /* Copy just this one partition table entry. */
430 s
=sys_safecopyfrom(mp
->IO_ENDPT
, (vir_bytes
) mp
->IO_GRANT
,
431 0, (vir_bytes
) &entry
, sizeof(entry
), D
);
433 s
=sys_datacopy(mp
->IO_ENDPT
, (vir_bytes
) mp
->ADDRESS
,
434 SELF
, (vir_bytes
) &entry
, sizeof(entry
));
438 dv
->dv_base
= entry
.base
;
439 dv
->dv_size
= entry
.size
;
441 /* Return a partition table entry and the geometry of the drive. */
442 entry
.base
= dv
->dv_base
;
443 entry
.size
= dv
->dv_size
;
444 (*dp
->dr_geometry
)(&entry
);
446 s
=sys_safecopyto(mp
->IO_ENDPT
, (vir_bytes
) mp
->IO_GRANT
,
447 0, (vir_bytes
) &entry
, sizeof(entry
), D
);
449 s
=sys_datacopy(SELF
, (vir_bytes
) &entry
,
450 mp
->IO_ENDPT
, (vir_bytes
) mp
->ADDRESS
, sizeof(entry
));
458 /*===========================================================================*
460 *===========================================================================*/
461 PUBLIC
int mq_queue(message
*m
)
466 panic("libdriver","mq_queue: mq_get failed", NO_NUM
);
467 memcpy(&mq
->mq_mess
, m
, sizeof(mq
->mq_mess
));
472 for(mi
= queue_head
; mi
->mq_next
; mi
= mi
->mq_next
)