[4] | 1 | /* This file contains device independent device driver interface.
|
---|
| 2 | *
|
---|
| 3 | * Changes:
|
---|
| 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)
|
---|
| 8 | *
|
---|
| 9 | *
|
---|
| 10 | * The drivers support the following operations (using message format m2):
|
---|
| 11 | *
|
---|
| 12 | * m_type DEVICE PROC_NR 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 | *
|
---|
| 33 | * The file contains one entry point:
|
---|
| 34 | *
|
---|
| 35 | * driver_task: called by the device dependent task entry
|
---|
| 36 | */
|
---|
| 37 |
|
---|
| 38 | #include "../drivers.h"
|
---|
| 39 | #include <sys/ioc_disk.h>
|
---|
| 40 | #include "driver.h"
|
---|
| 41 |
|
---|
| 42 | #define BUF_EXTRA 0
|
---|
| 43 |
|
---|
| 44 | /* Claim space for variables. */
|
---|
| 45 | PRIVATE u8_t buffer[(unsigned) 2 * DMA_BUF_SIZE + BUF_EXTRA];
|
---|
| 46 | u8_t *tmp_buf; /* the DMA buffer eventually */
|
---|
| 47 | phys_bytes tmp_phys; /* phys address of DMA buffer */
|
---|
| 48 |
|
---|
| 49 | FORWARD _PROTOTYPE( void init_buffer, (void) );
|
---|
| 50 | FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp) );
|
---|
| 51 | FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp) );
|
---|
| 52 |
|
---|
| 53 | int device_caller;
|
---|
| 54 |
|
---|
| 55 | /*===========================================================================*
|
---|
| 56 | * driver_task *
|
---|
| 57 | *===========================================================================*/
|
---|
| 58 | PUBLIC void driver_task(dp)
|
---|
| 59 | struct driver *dp; /* Device dependent entry points. */
|
---|
| 60 | {
|
---|
| 61 | /* Main program of any device driver task. */
|
---|
| 62 |
|
---|
| 63 | int r, proc_nr;
|
---|
| 64 | message mess;
|
---|
| 65 |
|
---|
| 66 | /* Get a DMA buffer. */
|
---|
| 67 | init_buffer();
|
---|
| 68 |
|
---|
| 69 | /* Here is the main loop of the disk task. It waits for a message, carries
|
---|
| 70 | * it out, and sends a reply.
|
---|
| 71 | */
|
---|
| 72 | while (TRUE) {
|
---|
| 73 |
|
---|
| 74 | /* Wait for a request to read or write a disk block. */
|
---|
| 75 | if(receive(ANY, &mess) != OK) continue;
|
---|
| 76 |
|
---|
| 77 | device_caller = mess.m_source;
|
---|
| 78 | proc_nr = mess.PROC_NR;
|
---|
| 79 |
|
---|
| 80 | /* Now carry out the work. */
|
---|
| 81 | switch(mess.m_type) {
|
---|
| 82 | case DEV_OPEN: r = (*dp->dr_open)(dp, &mess); break;
|
---|
| 83 | case DEV_CLOSE: r = (*dp->dr_close)(dp, &mess); break;
|
---|
| 84 | case DEV_IOCTL: r = (*dp->dr_ioctl)(dp, &mess); break;
|
---|
| 85 | case CANCEL: r = (*dp->dr_cancel)(dp, &mess);break;
|
---|
| 86 | case DEV_SELECT: r = (*dp->dr_select)(dp, &mess);break;
|
---|
| 87 |
|
---|
| 88 | case DEV_READ:
|
---|
| 89 | case DEV_WRITE: r = do_rdwt(dp, &mess); break;
|
---|
| 90 | case DEV_GATHER:
|
---|
| 91 | case DEV_SCATTER: r = do_vrdwt(dp, &mess); break;
|
---|
| 92 |
|
---|
| 93 | case HARD_INT: /* leftover interrupt or expired timer. */
|
---|
| 94 | if(dp->dr_hw_int) {
|
---|
| 95 | (*dp->dr_hw_int)(dp, &mess);
|
---|
| 96 | }
|
---|
| 97 | continue;
|
---|
| 98 | case SYS_SIG: (*dp->dr_signal)(dp, &mess);
|
---|
| 99 | continue; /* don't reply */
|
---|
| 100 | case SYN_ALARM: (*dp->dr_alarm)(dp, &mess);
|
---|
| 101 | continue; /* don't reply */
|
---|
| 102 | default:
|
---|
| 103 | if(dp->dr_other)
|
---|
| 104 | r = (*dp->dr_other)(dp, &mess);
|
---|
| 105 | else
|
---|
| 106 | r = EINVAL;
|
---|
| 107 | break;
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | /* Clean up leftover state. */
|
---|
| 111 | (*dp->dr_cleanup)();
|
---|
| 112 |
|
---|
| 113 | /* Finally, prepare and send the reply message. */
|
---|
| 114 | if (r != EDONTREPLY) {
|
---|
| 115 | mess.m_type = TASK_REPLY;
|
---|
| 116 | mess.REP_PROC_NR = proc_nr;
|
---|
| 117 | /* Status is # of bytes transferred or error code. */
|
---|
| 118 | mess.REP_STATUS = r;
|
---|
| 119 | send(device_caller, &mess);
|
---|
| 120 | }
|
---|
| 121 | }
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | /*===========================================================================*
|
---|
| 125 | * init_buffer *
|
---|
| 126 | *===========================================================================*/
|
---|
| 127 | PRIVATE void init_buffer()
|
---|
| 128 | {
|
---|
| 129 | /* Select a buffer that can safely be used for DMA transfers. It may also
|
---|
| 130 | * be used to read partition tables and such. Its absolute address is
|
---|
| 131 | * 'tmp_phys', the normal address is 'tmp_buf'.
|
---|
| 132 | */
|
---|
| 133 |
|
---|
| 134 | unsigned left;
|
---|
| 135 |
|
---|
| 136 | tmp_buf = buffer;
|
---|
| 137 | sys_umap(SELF, D, (vir_bytes)buffer, (phys_bytes)sizeof(buffer), &tmp_phys);
|
---|
| 138 |
|
---|
| 139 | if ((left = dma_bytes_left(tmp_phys)) < DMA_BUF_SIZE) {
|
---|
| 140 | /* First half of buffer crosses a 64K boundary, can't DMA into that */
|
---|
| 141 | tmp_buf += left;
|
---|
| 142 | tmp_phys += left;
|
---|
| 143 | }
|
---|
| 144 | }
|
---|
| 145 |
|
---|
| 146 | /*===========================================================================*
|
---|
| 147 | * do_rdwt *
|
---|
| 148 | *===========================================================================*/
|
---|
| 149 | PRIVATE int do_rdwt(dp, mp)
|
---|
| 150 | struct driver *dp; /* device dependent entry points */
|
---|
| 151 | message *mp; /* pointer to read or write message */
|
---|
| 152 | {
|
---|
| 153 | /* Carry out a single read or write request. */
|
---|
| 154 | iovec_t iovec1;
|
---|
| 155 | int r, opcode;
|
---|
| 156 | phys_bytes phys_addr;
|
---|
| 157 |
|
---|
| 158 | /* Disk address? Address and length of the user buffer? */
|
---|
| 159 | if (mp->COUNT < 0) return(EINVAL);
|
---|
| 160 |
|
---|
| 161 | /* Check the user buffer. */
|
---|
| 162 | sys_umap(mp->PROC_NR, D, (vir_bytes) mp->ADDRESS, mp->COUNT, &phys_addr);
|
---|
| 163 | if (phys_addr == 0) return(EFAULT);
|
---|
| 164 |
|
---|
| 165 | /* Prepare for I/O. */
|
---|
| 166 | if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
|
---|
| 167 |
|
---|
| 168 | /* Create a one element scatter/gather vector for the buffer. */
|
---|
| 169 | opcode = mp->m_type == DEV_READ ? DEV_GATHER : DEV_SCATTER;
|
---|
| 170 | iovec1.iov_addr = (vir_bytes) mp->ADDRESS;
|
---|
| 171 | iovec1.iov_size = mp->COUNT;
|
---|
| 172 |
|
---|
| 173 | /* Transfer bytes from/to the device. */
|
---|
| 174 | r = (*dp->dr_transfer)(mp->PROC_NR, opcode, mp->POSITION, &iovec1, 1);
|
---|
| 175 |
|
---|
| 176 | /* Return the number of bytes transferred or an error code. */
|
---|
| 177 | return(r == OK ? (mp->COUNT - iovec1.iov_size) : r);
|
---|
| 178 | }
|
---|
| 179 |
|
---|
| 180 | /*==========================================================================*
|
---|
| 181 | * do_vrdwt *
|
---|
| 182 | *==========================================================================*/
|
---|
| 183 | PRIVATE int do_vrdwt(dp, mp)
|
---|
| 184 | struct driver *dp; /* device dependent entry points */
|
---|
| 185 | message *mp; /* pointer to read or write message */
|
---|
| 186 | {
|
---|
| 187 | /* Carry out an device read or write to/from a vector of user addresses.
|
---|
| 188 | * The "user addresses" are assumed to be safe, i.e. FS transferring to/from
|
---|
| 189 | * its own buffers, so they are not checked.
|
---|
| 190 | */
|
---|
| 191 | static iovec_t iovec[NR_IOREQS];
|
---|
| 192 | iovec_t *iov;
|
---|
| 193 | phys_bytes iovec_size;
|
---|
| 194 | unsigned nr_req;
|
---|
| 195 | int r;
|
---|
| 196 |
|
---|
| 197 | nr_req = mp->COUNT; /* Length of I/O vector */
|
---|
| 198 |
|
---|
| 199 | if (mp->m_source < 0) {
|
---|
| 200 | /* Called by a task, no need to copy vector. */
|
---|
| 201 | iov = (iovec_t *) mp->ADDRESS;
|
---|
| 202 | } else {
|
---|
| 203 | /* Copy the vector from the caller to kernel space. */
|
---|
| 204 | if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
|
---|
| 205 | iovec_size = (phys_bytes) (nr_req * sizeof(iovec[0]));
|
---|
| 206 |
|
---|
| 207 | if (OK != sys_datacopy(mp->m_source, (vir_bytes) mp->ADDRESS,
|
---|
| 208 | SELF, (vir_bytes) iovec, iovec_size))
|
---|
| 209 | panic((*dp->dr_name)(),"bad I/O vector by", mp->m_source);
|
---|
| 210 | iov = iovec;
|
---|
| 211 | }
|
---|
| 212 |
|
---|
| 213 | /* Prepare for I/O. */
|
---|
| 214 | if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO);
|
---|
| 215 |
|
---|
| 216 | /* Transfer bytes from/to the device. */
|
---|
| 217 | r = (*dp->dr_transfer)(mp->PROC_NR, mp->m_type, mp->POSITION, iov, nr_req);
|
---|
| 218 |
|
---|
| 219 | /* Copy the I/O vector back to the caller. */
|
---|
| 220 | if (mp->m_source >= 0) {
|
---|
| 221 | sys_datacopy(SELF, (vir_bytes) iovec,
|
---|
| 222 | mp->m_source, (vir_bytes) mp->ADDRESS, iovec_size);
|
---|
| 223 | }
|
---|
| 224 | return(r);
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | /*===========================================================================*
|
---|
| 228 | * no_name *
|
---|
| 229 | *===========================================================================*/
|
---|
| 230 | PUBLIC char *no_name()
|
---|
| 231 | {
|
---|
| 232 | /* Use this default name if there is no specific name for the device. This was
|
---|
| 233 | * originally done by fetching the name from the task table for this process:
|
---|
| 234 | * "return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);", but currently a
|
---|
| 235 | * real "noname" is returned. Perhaps, some system information service can be
|
---|
| 236 | * queried for a name at a later time.
|
---|
| 237 | */
|
---|
| 238 | static char name[] = "noname";
|
---|
| 239 | return name;
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | /*============================================================================*
|
---|
| 243 | * do_nop *
|
---|
| 244 | *============================================================================*/
|
---|
| 245 | PUBLIC int do_nop(dp, mp)
|
---|
| 246 | struct driver *dp;
|
---|
| 247 | message *mp;
|
---|
| 248 | {
|
---|
| 249 | /* Nothing there, or nothing to do. */
|
---|
| 250 |
|
---|
| 251 | switch (mp->m_type) {
|
---|
| 252 | case DEV_OPEN: return(ENODEV);
|
---|
| 253 | case DEV_CLOSE: return(OK);
|
---|
| 254 | case DEV_IOCTL: return(ENOTTY);
|
---|
| 255 | default: return(EIO);
|
---|
| 256 | }
|
---|
| 257 | }
|
---|
| 258 |
|
---|
| 259 | /*============================================================================*
|
---|
| 260 | * nop_signal *
|
---|
| 261 | *============================================================================*/
|
---|
| 262 | PUBLIC void nop_signal(dp, mp)
|
---|
| 263 | struct driver *dp;
|
---|
| 264 | message *mp;
|
---|
| 265 | {
|
---|
| 266 | /* Default action for signal is to ignore. */
|
---|
| 267 | }
|
---|
| 268 |
|
---|
| 269 | /*============================================================================*
|
---|
| 270 | * nop_alarm *
|
---|
| 271 | *============================================================================*/
|
---|
| 272 | PUBLIC void nop_alarm(dp, mp)
|
---|
| 273 | struct driver *dp;
|
---|
| 274 | message *mp;
|
---|
| 275 | {
|
---|
| 276 | /* Ignore the leftover alarm. */
|
---|
| 277 | }
|
---|
| 278 |
|
---|
| 279 | /*===========================================================================*
|
---|
| 280 | * nop_prepare *
|
---|
| 281 | *===========================================================================*/
|
---|
| 282 | PUBLIC struct device *nop_prepare(device)
|
---|
| 283 | {
|
---|
| 284 | /* Nothing to prepare for. */
|
---|
| 285 | return(NIL_DEV);
|
---|
| 286 | }
|
---|
| 287 |
|
---|
| 288 | /*===========================================================================*
|
---|
| 289 | * nop_cleanup *
|
---|
| 290 | *===========================================================================*/
|
---|
| 291 | PUBLIC void nop_cleanup()
|
---|
| 292 | {
|
---|
| 293 | /* Nothing to clean up. */
|
---|
| 294 | }
|
---|
| 295 |
|
---|
| 296 | /*===========================================================================*
|
---|
| 297 | * nop_cancel *
|
---|
| 298 | *===========================================================================*/
|
---|
| 299 | PUBLIC int nop_cancel(struct driver *dr, message *m)
|
---|
| 300 | {
|
---|
| 301 | /* Nothing to do for cancel. */
|
---|
| 302 | return(OK);
|
---|
| 303 | }
|
---|
| 304 |
|
---|
| 305 | /*===========================================================================*
|
---|
| 306 | * nop_select *
|
---|
| 307 | *===========================================================================*/
|
---|
| 308 | PUBLIC int nop_select(struct driver *dr, message *m)
|
---|
| 309 | {
|
---|
| 310 | /* Nothing to do for select. */
|
---|
| 311 | return(OK);
|
---|
| 312 | }
|
---|
| 313 |
|
---|
| 314 | /*============================================================================*
|
---|
| 315 | * do_diocntl *
|
---|
| 316 | *============================================================================*/
|
---|
| 317 | PUBLIC int do_diocntl(dp, mp)
|
---|
| 318 | struct driver *dp;
|
---|
| 319 | message *mp; /* pointer to ioctl request */
|
---|
| 320 | {
|
---|
| 321 | /* Carry out a partition setting/getting request. */
|
---|
| 322 | struct device *dv;
|
---|
| 323 | struct partition entry;
|
---|
| 324 | int s;
|
---|
| 325 |
|
---|
| 326 | if (mp->REQUEST != DIOCSETP && mp->REQUEST != DIOCGETP) {
|
---|
| 327 | if(dp->dr_other) {
|
---|
| 328 | return dp->dr_other(dp, mp);
|
---|
| 329 | } else return(ENOTTY);
|
---|
| 330 | }
|
---|
| 331 |
|
---|
| 332 | /* Decode the message parameters. */
|
---|
| 333 | if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NIL_DEV) return(ENXIO);
|
---|
| 334 |
|
---|
| 335 | if (mp->REQUEST == DIOCSETP) {
|
---|
| 336 | /* Copy just this one partition table entry. */
|
---|
| 337 | if (OK != (s=sys_datacopy(mp->PROC_NR, (vir_bytes) mp->ADDRESS,
|
---|
| 338 | SELF, (vir_bytes) &entry, sizeof(entry))))
|
---|
| 339 | return s;
|
---|
| 340 | dv->dv_base = entry.base;
|
---|
| 341 | dv->dv_size = entry.size;
|
---|
| 342 | } else {
|
---|
| 343 | /* Return a partition table entry and the geometry of the drive. */
|
---|
| 344 | entry.base = dv->dv_base;
|
---|
| 345 | entry.size = dv->dv_size;
|
---|
| 346 | (*dp->dr_geometry)(&entry);
|
---|
| 347 | if (OK != (s=sys_datacopy(SELF, (vir_bytes) &entry,
|
---|
| 348 | mp->PROC_NR, (vir_bytes) mp->ADDRESS, sizeof(entry))))
|
---|
| 349 | return s;
|
---|
| 350 | }
|
---|
| 351 | return(OK);
|
---|
| 352 | }
|
---|