Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/drivers/char/lp.c 3 * 4 * Copyright 2018, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/asm.h> 9 #include <fiwix/devices.h> 10 #include <fiwix/fs.h> 11 #include <fiwix/errno.h> 12 #include <fiwix/lp.h> 13 #include <fiwix/stdio.h> 14 #include <fiwix/string.h> 15 16 struct lp lp_table[LP_MINORS]; 17 18 static struct fs_operations lp_driver_fsop = { 19 0, 20 0, 21 22 lp_open, 23 lp_close, 24 NULL, /* read */ 25 lp_write, 26 NULL, /* ioctl */ 27 NULL, /* lseek */ 28 NULL, /* readdir */ 29 NULL, /* mmap */ 30 NULL, /* select */ 31 32 NULL, /* readlink */ 33 NULL, /* followlink */ 34 NULL, /* bmap */ 35 NULL, /* lockup */ 36 NULL, /* rmdir */ 37 NULL, /* link */ 38 NULL, /* unlink */ 39 NULL, /* symlink */ 40 NULL, /* mkdir */ 41 NULL, /* mknod */ 42 NULL, /* truncate */ 43 NULL, /* create */ 44 NULL, /* rename */ 45 46 NULL, /* read_block */ 47 NULL, /* write_block */ 48 49 NULL, /* read_inode */ 50 NULL, /* write_inode */ 51 NULL, /* ialloc */ 52 NULL, /* ifree */ 53 NULL, /* statfs */ 54 NULL, /* read_superblock */ 55 NULL, /* remount_fs */ 56 NULL, /* write_superblock */ 57 NULL /* release_superblock */ 58 }; 59 60 static struct device lp_device = { 61 "lp", 62 LP_MAJOR, 63 { 0, 0, 0, 0, 0, 0, 0, 0 }, 64 0, 65 NULL, 66 &lp_driver_fsop, 67 NULL 68 }; 69 70 struct lp lp_table[LP_MINORS] = { 71 { LP0_ADDR, LP0_ADDR + 1, LP0_ADDR + 2, 0 } 72 }; 73 74 static void lp_delay(void) 75 { 76 int n; 77 78 for(n = 0; n < 10000; n++) { 79 NOP(); 80 } 81 } 82 83 static int lp_ready(int minor) 84 { 85 int n; 86 87 for(n = 0; n < LP_RDY_RETR; n++) { 88 if(inport_b(lp_table[minor].stat) & LP_STAT_BUS) { 89 break; 90 } 91 lp_delay(); 92 } 93 if(n == LP_RDY_RETR) { 94 return 0; 95 } 96 return 1; 97 } 98 99 static int lp_probe(int minor) 100 { 101 /* first check */ 102 outport_b(lp_table[minor].data, 0x55); 103 lp_delay(); 104 if(inport_b(lp_table[minor].data) != 0x55) { 105 return 1; /* did not retain data */ 106 } 107 108 /* second check */ 109 outport_b(lp_table[minor].data, 0xAA); 110 lp_delay(); 111 if(inport_b(lp_table[minor].data) != 0xAA) { 112 return 1; /* did not retain data */ 113 } 114 return 0; 115 } 116 117 static int lp_write_data(int minor, unsigned char c) 118 { 119 unsigned char ctrl; 120 121 if(!lp_ready(minor)) { 122 return -EBUSY; 123 } 124 outport_b(lp_table[minor].data, c); 125 ctrl = inport_b(lp_table[minor].ctrl); 126 outport_b(lp_table[minor].ctrl, ctrl | LP_CTRL_STR); 127 lp_delay(); 128 outport_b(lp_table[minor].ctrl, ctrl); 129 if(!lp_ready(minor)) { 130 return -EBUSY; 131 } 132 return 1; 133 } 134 135 int lp_open(struct inode *i, struct fd *fd_table) 136 { 137 int minor; 138 139 minor = MINOR(i->rdev); 140 if(!TEST_MINOR(lp_device.minors, minor)) { 141 return -ENXIO; 142 } 143 if(!(lp_table[minor].flags & LP_CTRL_SEL)) { 144 return -ENXIO; 145 } 146 if(lp_table[minor].flags & LP_STAT_BUS) { 147 return -EBUSY; 148 } 149 lp_table[minor].flags |= LP_STAT_BUS; 150 return 0; 151 } 152 153 int lp_close(struct inode *i, struct fd *fd_table) 154 { 155 int minor; 156 157 minor = MINOR(i->rdev); 158 if(!TEST_MINOR(lp_device.minors, minor)) { 159 return -ENXIO; 160 } 161 lp_table[minor].flags &= ~LP_STAT_BUS; 162 return 0; 163 } 164 165 int lp_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) 166 { 167 unsigned int n; 168 int bytes_written, total_written; 169 int minor; 170 171 minor = MINOR(i->rdev); 172 if(!TEST_MINOR(lp_device.minors, minor)) { 173 return -ENXIO; 174 } 175 176 total_written = 0; 177 for(n = 0; n < count; n++) { 178 bytes_written = lp_write_data(minor, buffer[n]); 179 if(bytes_written != 1) { 180 break; 181 } 182 total_written += bytes_written; 183 } 184 185 return total_written; 186 } 187 188 void lp_init(void) 189 { 190 int n; 191 unsigned char ctrl; 192 193 for(n = 0; n < LP_MINORS; n++) { 194 if(!lp_probe(n)) { 195 ctrl = inport_b(lp_table[n].ctrl); 196 ctrl &= ~LP_CTRL_AUT; /* disable auto LF */ 197 ctrl |= LP_CTRL_INI; /* initialize */ 198 ctrl |= LP_CTRL_SEL; /* select in */ 199 ctrl &= ~LP_CTRL_IRQ; /* disable IRQ */ 200 ctrl &= ~LP_CTRL_BID; /* disable bidirectional mode */ 201 outport_b(lp_table[n].ctrl, ctrl); 202 lp_table[n].flags |= LP_CTRL_SEL; 203 printk("lp%d 0x%04X-0x%04X - \n", n, lp_table[n].data, lp_table[n].data + 2); 204 SET_MINOR(lp_device.minors, n); 205 } 206 } 207 208 for(n = 0; n < LP_MINORS; n++) { 209 if(lp_table[n].flags & LP_CTRL_SEL) { 210 if(register_device(CHR_DEV, &lp_device)) { 211 printk("WARNING: %s(): unable to register lp device.\n", __FUNCTION__); 212 } 213 break; 214 } 215 } 216 }