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