1: /* This file contains device independent device driver interface. 2: * Author: Kees J. Bot. 3: * 4: * The drivers support the following operations (using message format m2): 5: * 6: * m_type DEVICE PROC_NR COUNT POSITION ADRRESS 7: * ---------------------------------------------------------------- 8: * | DEV_OPEN | device | proc nr | | | | 9: * |------------+---------+---------+---------+---------+---------| 10: * | DEV_CLOSE | device | proc nr | | | | 11: * |------------+---------+---------+---------+---------+---------| 12: * | DEV_READ | device | proc nr | bytes | offset | buf ptr | 13: * |------------+---------+---------+---------+---------+---------| 14: * | DEV_WRITE | device | proc nr | bytes | offset | buf ptr | 15: * |------------+---------+---------+---------+---------+---------| 16: * | DEV_GATHER | device | proc nr | iov len | offset | iov ptr | 17: * |------------+---------+---------+---------+---------+---------| 18: * | DEV_SCATTER| device | proc nr | iov len | offset | iov ptr | 19: * |------------+---------+---------+---------+---------+---------| 20: * | DEV_IOCTL | device | proc nr |func code| | buf ptr | 21: * ---------------------------------------------------------------- 22: * 23: * The file contains one entry point: 24: * 25: * driver_task: called by the device dependent task entry 26: * 27: * 28: * Constructed 92/04/02 by Kees J. Bot from the old AT wini and floppy driver. 29: */ 30: 31: #include "kernel.h" 32: #include <sys/ioc_disk.h> 33: #include "driver.h" 34: 35: #if (CHIP == INTEL) 36: #if ENABLE_AHA1540_SCSI && DMA_BUF_SIZE < 2048 37: /* A bit extra scratch for the Adaptec driver. */ 38: #define BUF_EXTRA (2048 - DMA_BUF_SIZE) 39: #else 40: #define BUF_EXTRA 0 41: #endif 42: 43: /* Claim space for variables. */ 44: PRIVATE u8_t buffer[(unsigned) 2 * DMA_BUF_SIZE + BUF_EXTRA]; 45: u8_t *tmp_buf; /* the DMA buffer eventually */ 46: phys_bytes tmp_phys; /* phys address of DMA buffer */ 47: 48: #else /* CHIP != INTEL */ 49: 50: /* Claim space for variables. */ 51: u8_t tmp_buf[DMA_BUF_SIZE]; /* the DMA buffer */ 52: phys_bytes tmp_phys; /* phys address of DMA buffer */ 53: 54: #endif /* CHIP != INTEL */ 55: 56: FORWARD _PROTOTYPE( void init_buffer, (void) ); 57: FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp) ); 58: FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp) ); 59: 60: 61: /*===========================================================================* 62: * driver_task * 63: *===========================================================================*/ 64: PUBLIC void driver_task(dp) 65: struct driver *dp; /* Device dependent entry points. */ 66: { 67: /* Main program of any device driver task. */ 68: 69: int r, caller, proc_nr; 70: message mess; 71: 72: init_buffer(); /* Get a DMA buffer. */ 73: 74: 75: /* Here is the main loop of the disk task. It waits for a message, carries 76: * it out, and sends a reply. 77: */ 78: 79: while (TRUE) { 80: /* Check if a timer expired. */ 81: if (proc_ptr->p_exptimers != NULL) tmr_exptimers(); 82: 83: /* Wait for a request to read or write a disk block. */ 84: receive(ANY, &mess); 85: 86: caller = mess.m_source; 87: proc_nr = mess.PROC_NR; 88: 89: /* Check if legitimate caller: FS or a task. */ 90: if (caller != FS_PROC_NR && caller >= 0) { 91: printf("%s: got message from %d\n", (*dp->dr_name)(), caller); 92: continue; 93: } 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: 101: case DEV_READ: 102: case DEV_WRITE: r = do_rdwt(dp, &mess); break; 103: 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: continue; 109: 110: default: r = EINVAL; break; 111: } 112: 113: /* Clean up leftover state. */ 114: (*dp->dr_cleanup)(); 115: 116: /* Finally, prepare and send the reply message. */ 117: mess.m_type = TASK_REPLY; 118: mess.REP_PROC_NR = proc_nr; 119: 120: mess.REP_STATUS = r; /* # of bytes transferred or error code */ 121: send(caller, &mess); /* send reply to caller */ 122: } 123: } 124: 125: 126: /*===========================================================================* 127: * init_buffer * 128: *===========================================================================*/ 129: PRIVATE void init_buffer() 130: { 131: /* Select a buffer that can safely be used for dma transfers. It may also 132: * be used to read partition tables and such. Its absolute address is 133: * 'tmp_phys', the normal address is 'tmp_buf'. 134: */ 135: 136: #if (CHIP == INTEL) 137: unsigned left; 138: 139: tmp_buf = buffer; 140: tmp_phys = vir2phys(buffer); 141: 142: if ((left = dma_bytes_left(tmp_phys)) < DMA_BUF_SIZE) { 143: /* First half of buffer crosses a 64K boundary, can't DMA into that */ 144: tmp_buf += left; 145: tmp_phys += left; 146: } 147: #else /* CHIP != INTEL */ 148: tmp_phys = vir2phys(tmp_buf); 149: #endif /* CHIP != INTEL */ 150: } 151: 152: 153: /*===========================================================================* 154: * do_rdwt * 155: *===========================================================================*/ 156: PRIVATE int do_rdwt(dp, mp) 157: struct driver *dp; /* device dependent entry points */ 158: message *mp; /* pointer to read or write message */ 159: { 160: /* Carry out a single read or write request. */ 161: iovec_t iovec1; 162: int r, opcode; 163: phys_bytes phys_addr; 164: 165: /* Disk address? Address and length of the user buffer? */ 166: if (mp->COUNT < 0) return(EINVAL); 167: 168: /* Check the user buffer. */ 169: phys_addr = numap(mp->PROC_NR, (vir_bytes) mp->ADDRESS, mp->COUNT); 170: if (phys_addr == 0) return(EFAULT); 171: 172: /* Prepare for I/O. */ 173: if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO); 174: 175: /* Create a one element scatter/gather vector for the buffer. */ 176: opcode = mp->m_type == DEV_READ ? DEV_GATHER : DEV_SCATTER; 177: iovec1.iov_addr = (vir_bytes) mp->ADDRESS; 178: iovec1.iov_size = mp->COUNT; 179: 180: /* Transfer bytes from/to the device. */ 181: r = (*dp->dr_transfer)(mp->PROC_NR, opcode, mp->POSITION, &iovec1, 1); 182: 183: /* Return the number of bytes transferred or an error code. */ 184: return(r == OK ? (mp->COUNT - iovec1.iov_size) : r); 185: } 186: 187: 188: /*==========================================================================* 189: * do_vrdwt * 190: *==========================================================================*/ 191: PRIVATE int do_vrdwt(dp, mp) 192: struct driver *dp; /* device dependent entry points */ 193: message *mp; /* pointer to read or write message */ 194: { 195: /* Carry out an device read or write to/from a vector of user addresses. 196: * The "user addresses" are assumed to be safe, i.e. FS transferring to/from 197: * its own buffers, so they are not checked. 198: */ 199: static iovec_t iovec[NR_IOREQS]; 200: iovec_t *iov; 201: phys_bytes iovec_phys, user_iovec_phys; 202: size_t iovec_size; 203: unsigned nr_req; 204: int r; 205: 206: nr_req = mp->COUNT; /* Length of I/O vector */ 207: 208: if (mp->m_source < 0) { 209: /* Called by a task, no need to copy vector. */ 210: iov = (iovec_t *) mp->ADDRESS; 211: } else { 212: /* Copy the vector from the caller to kernel space. */ 213: if (nr_req > NR_IOREQS) nr_req = NR_IOREQS; 214: iovec_size = nr_req * sizeof(iovec[0]); 215: user_iovec_phys = numap(mp->m_source, 216: (vir_bytes) mp->ADDRESS, iovec_size); 217: if (user_iovec_phys == 0) panic("bad I/O vector by", mp->m_source); 218: 219: iovec_phys = vir2phys(iovec); 220: phys_copy(user_iovec_phys, iovec_phys, (phys_bytes) iovec_size); 221: iov = iovec; 222: } 223: 224: /* Prepare for I/O. */ 225: if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO); 226: 227: /* Transfer bytes from/to the device. */ 228: r = (*dp->dr_transfer)(mp->PROC_NR, mp->m_type, mp->POSITION, iov, nr_req); 229: 230: /* Copy the I/O vector back to the caller. */ 231: if (mp->m_source >= 0) { 232: phys_copy(iovec_phys, user_iovec_phys, (phys_bytes) iovec_size); 233: } 234: return(r); 235: } 236: 237: 238: /*===========================================================================* 239: * no_name * 240: *===========================================================================*/ 241: PUBLIC char *no_name() 242: { 243: /* If no specific name for the device. */ 244: 245: return(tasktab[proc_number(proc_ptr) + NR_TASKS].name); 246: } 247: 248: 249: /*============================================================================* 250: * do_nop * 251: *============================================================================*/ 252: PUBLIC int do_nop(dp, mp) 253: struct driver *dp; 254: message *mp; 255: { 256: /* Nothing there, or nothing to do. */ 257: 258: switch (mp->m_type) { 259: case DEV_OPEN: return(ENODEV); 260: case DEV_CLOSE: return(OK); 261: case DEV_IOCTL: return(ENOTTY); 262: default: return(EIO); 263: } 264: } 265: 266: 267: /*===========================================================================* 268: * nop_prepare * 269: *===========================================================================*/ 270: PUBLIC struct device *nop_prepare(device) 271: { 272: /* Nothing to prepare for. */ 273: return(NIL_DEV); 274: } 275: 276: 277: 278: /*===========================================================================* 279: * nop_cleanup * 280: *===========================================================================*/ 281: PUBLIC void nop_cleanup() 282: { 283: /* Nothing to clean up. */ 284: } 285: 286: 287: 288: /*===========================================================================* 289: * nop_task * 290: *===========================================================================*/ 291: PUBLIC void nop_task() 292: { 293: /* Unused controllers are "serviced" by this task. */ 294: struct driver nop_tab = { 295: no_name, 296: do_nop, 297: do_nop, 298: do_nop, 299: nop_prepare, 300: NULL, 301: nop_cleanup, 302: NULL, 303: }; 304: driver_task(&nop_tab); 305: } 306: 307: 308: /*============================================================================* 309: * do_diocntl * 310: *============================================================================*/ 311: PUBLIC int do_diocntl(dp, mp) 312: struct driver *dp; 313: message *mp; /* pointer to ioctl request */ 314: { 315: /* Carry out a partition setting/getting request. */ 316: struct device *dv; 317: phys_bytes user_phys, entry_phys; 318: struct partition entry; 319: 320: if (mp->REQUEST != DIOCSETP && mp->REQUEST != DIOCGETP) return(ENOTTY); 321: 322: /* Decode the message parameters. */ 323: if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NIL_DEV) return(ENXIO); 324: 325: user_phys = numap(mp->PROC_NR, (vir_bytes) mp->ADDRESS, sizeof(entry)); 326: if (user_phys == 0) return(EFAULT); 327: 328: entry_phys = vir2phys(&entry); 329: 330: if (mp->REQUEST == DIOCSETP) { 331: /* Copy just this one partition table entry. */ 332: phys_copy(user_phys, entry_phys, (phys_bytes) sizeof(entry)); 333: dv->dv_base = entry.base; 334: dv->dv_size = entry.size; 335: } else { 336: /* Return a partition table entry and the geometry of the drive. */ 337: entry.base = dv->dv_base; 338: entry.size = dv->dv_size; 339: (*dp->dr_geometry)(&entry); 340: phys_copy(entry_phys, user_phys, (phys_bytes) sizeof(entry)); 341: } 342: return(OK); 343: }