1 /* 2 * Copyright (c) 2007-2008 by Michael Lotz 3 * Heavily based on the original usb_serial driver which is: 4 * 5 * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li> 6 * Distributed under the terms of the MIT License. 7 */ 8 #include <KernelExport.h> 9 #include <Drivers.h> 10 #include <malloc.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 14 #include "Driver.h" 15 #include "SerialDevice.h" 16 17 static const char *sDeviceBaseName = "ports/serial"; 18 SerialDevice *gSerialDevices[DEVICES_COUNT]; 19 char *gDeviceNames[DEVICES_COUNT + 1]; 20 usb_module_info *gUSBModule = NULL; 21 tty_module_info *gTTYModule = NULL; 22 struct ddomain gSerialDomain; 23 sem_id gDriverLock = -1; 24 25 26 status_t 27 pc_serial_device_added(pc_device device, void **cookie) 28 { 29 TRACE_FUNCALLS("> pc_serial_device_added(0x%08x, 0x%08x)\n", device, cookie); 30 31 status_t status = B_OK; 32 const pc_device_descriptor *descriptor 33 = gUSBModule->get_device_descriptor(device); 34 35 TRACE_ALWAYS("probing device: 0x%04x/0x%04x\n", descriptor->vendor_id, 36 descriptor->product_id); 37 38 *cookie = NULL; 39 SerialDevice *serialDevice = SerialDevice::MakeDevice(device, 40 descriptor->vendor_id, descriptor->product_id); 41 42 const pc_configuration_info *configuration 43 = gUSBModule->get_nth_configuration(device, 0); 44 45 if (!configuration) 46 return B_ERROR; 47 48 status = serialDevice->AddDevice(configuration); 49 if (status < B_OK) { 50 delete serialDevice; 51 return status; 52 } 53 54 acquire_sem(gDriverLock); 55 for (int32 i = 0; i < DEVICES_COUNT; i++) { 56 if (gSerialDevices[i] != NULL) 57 continue; 58 59 status = serialDevice->Init(); 60 if (status < B_OK) { 61 delete serialDevice; 62 return status; 63 } 64 65 gSerialDevices[i] = serialDevice; 66 *cookie = serialDevice; 67 68 release_sem(gDriverLock); 69 TRACE_ALWAYS("%s (0x%04x/0x%04x) added\n", serialDevice->Description(), 70 descriptor->vendor_id, descriptor->product_id); 71 return B_OK; 72 } 73 74 release_sem(gDriverLock); 75 return B_ERROR; 76 } 77 78 79 status_t 80 pc_serial_device_removed(void *cookie) 81 { 82 TRACE_FUNCALLS("> pc_serial_device_removed(0x%08x)\n", cookie); 83 84 acquire_sem(gDriverLock); 85 86 SerialDevice *device = (SerialDevice *)cookie; 87 for (int32 i = 0; i < DEVICES_COUNT; i++) { 88 if (gSerialDevices[i] == device) { 89 if (device->IsOpen()) { 90 // the device will be deleted upon being freed 91 device->Removed(); 92 } else { 93 delete device; 94 gSerialDevices[i] = NULL; 95 } 96 break; 97 } 98 } 99 100 release_sem(gDriverLock); 101 TRACE_FUNCRET("< pc_serial_device_removed() returns\n"); 102 return B_OK; 103 } 104 105 106 //#pragma mark - 107 108 109 /* init_hardware - called once the first time the driver is loaded */ 110 status_t 111 init_hardware() 112 { 113 TRACE("init_hardware\n"); 114 return B_OK; 115 } 116 117 118 /* init_driver - called every time the driver is loaded. */ 119 status_t 120 init_driver() 121 { 122 load_settings(); 123 create_log_file(); 124 125 TRACE_FUNCALLS("> init_driver()\n"); 126 127 status_t status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule); 128 if (status < B_OK) 129 return status; 130 131 status = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule); 132 if (status < B_OK) { 133 put_module(B_TTY_MODULE_NAME); 134 return status; 135 } 136 137 for (int32 i = 0; i < DEVICES_COUNT; i++) 138 gSerialDevices[i] = NULL; 139 140 gDeviceNames[0] = NULL; 141 142 gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock"); 143 if (gDriverLock < B_OK) { 144 put_module(B_USB_MODULE_NAME); 145 put_module(B_TTY_MODULE_NAME); 146 return gDriverLock; 147 } 148 149 static pc_notify_hooks notifyHooks = { 150 &pc_serial_device_added, 151 &pc_serial_device_removed 152 }; 153 154 gUSBModule->register_driver(DRIVER_NAME, NULL, 0, NULL); 155 gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); 156 TRACE_FUNCRET("< init_driver() returns\n"); 157 return B_OK; 158 } 159 160 161 /* uninit_driver - called every time the driver is unloaded */ 162 void 163 uninit_driver() 164 { 165 TRACE_FUNCALLS("> uninit_driver()\n"); 166 167 gUSBModule->uninstall_notify(DRIVER_NAME); 168 acquire_sem(gDriverLock); 169 170 for (int32 i = 0; i < DEVICES_COUNT; i++) { 171 if (gSerialDevices[i]) { 172 delete gSerialDevices[i]; 173 gSerialDevices[i] = NULL; 174 } 175 } 176 177 for (int32 i = 0; gDeviceNames[i]; i++) 178 free(gDeviceNames[i]); 179 180 delete_sem(gDriverLock); 181 put_module(B_USB_MODULE_NAME); 182 put_module(B_TTY_MODULE_NAME); 183 184 TRACE_FUNCRET("< uninit_driver() returns\n"); 185 } 186 187 188 bool 189 pc_serial_service(struct tty *ptty, struct ddrover *ddr, uint flags) 190 { 191 TRACE_FUNCALLS("> pc_serial_service(0x%08x, 0x%08x, 0x%08x)\n", ptty, ddr, flags); 192 193 for (int32 i = 0; i < DEVICES_COUNT; i++) { 194 if (gSerialDevices[i] && gSerialDevices[i]->Service(ptty, ddr, flags)) { 195 TRACE_FUNCRET("< pc_serial_service() returns: true\n"); 196 return true; 197 } 198 } 199 200 TRACE_FUNCRET("< pc_serial_service() returns: false\n"); 201 return false; 202 } 203 204 205 /* pc_serial_open - handle open() calls */ 206 static status_t 207 pc_serial_open(const char *name, uint32 flags, void **cookie) 208 { 209 TRACE_FUNCALLS("> pc_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie); 210 acquire_sem(gDriverLock); 211 status_t status = ENODEV; 212 213 *cookie = NULL; 214 int i = strtol(name + strlen(sDeviceBaseName), NULL, 10); 215 if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) { 216 status = gSerialDevices[i]->Open(flags); 217 *cookie = gSerialDevices[i]; 218 } 219 220 release_sem(gDriverLock); 221 TRACE_FUNCRET("< pc_serial_open() returns: 0x%08x\n", status); 222 return status; 223 } 224 225 226 /* pc_serial_read - handle read() calls */ 227 static status_t 228 pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes) 229 { 230 TRACE_FUNCALLS("> pc_serial_read(0x%08x, %Ld, 0x%08x, %d)\n", cookie, 231 position, buffer, *numBytes); 232 SerialDevice *device = (SerialDevice *)cookie; 233 return device->Read((char *)buffer, numBytes); 234 } 235 236 237 /* pc_serial_write - handle write() calls */ 238 static status_t 239 pc_serial_write(void *cookie, off_t position, const void *buffer, 240 size_t *numBytes) 241 { 242 TRACE_FUNCALLS("> pc_serial_write(0x%08x, %Ld, 0x%08x, %d)\n", cookie, 243 position, buffer, *numBytes); 244 SerialDevice *device = (SerialDevice *)cookie; 245 return device->Write((const char *)buffer, numBytes); 246 } 247 248 249 /* pc_serial_control - handle ioctl calls */ 250 static status_t 251 pc_serial_control(void *cookie, uint32 op, void *arg, size_t length) 252 { 253 TRACE_FUNCALLS("> pc_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n", 254 cookie, op, arg, length); 255 SerialDevice *device = (SerialDevice *)cookie; 256 return device->Control(op, arg, length); 257 } 258 259 260 #if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__) 261 /* pc_serial_select - handle select start */ 262 static status_t 263 pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync) 264 { 265 TRACE_FUNCALLS("> pc_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n", 266 cookie, event, ref, sync); 267 SerialDevice *device = (SerialDevice *)cookie; 268 return device->Select(event, ref, sync); 269 } 270 271 272 /* pc_serial_deselect - handle select exit */ 273 static status_t 274 pc_serial_deselect(void *cookie, uint8 event, selectsync *sync) 275 { 276 TRACE_FUNCALLS("> pc_serial_deselect(0x%08x, 0x%08x, %p)\n", 277 cookie, event, sync); 278 SerialDevice *device = (SerialDevice *)cookie; 279 return device->DeSelect(event, sync); 280 } 281 #endif // DANO, HAIKU 282 283 284 /* pc_serial_close - handle close() calls */ 285 static status_t 286 pc_serial_close(void *cookie) 287 { 288 TRACE_FUNCALLS("> pc_serial_close(0x%08x)\n", cookie); 289 SerialDevice *device = (SerialDevice *)cookie; 290 return device->Close(); 291 } 292 293 294 /* pc_serial_free - called after last device is closed, and all i/o complete. */ 295 static status_t 296 pc_serial_free(void *cookie) 297 { 298 TRACE_FUNCALLS("> pc_serial_free(0x%08x)\n", cookie); 299 SerialDevice *device = (SerialDevice *)cookie; 300 acquire_sem(gDriverLock); 301 status_t status = device->Free(); 302 if (device->IsRemoved()) { 303 for (int32 i = 0; i < DEVICES_COUNT; i++) { 304 if (gSerialDevices[i] == device) { 305 // the device is removed already but as it was open the 306 // removed hook has not deleted the object 307 delete device; 308 gSerialDevices[i] = NULL; 309 break; 310 } 311 } 312 } 313 314 release_sem(gDriverLock); 315 return status; 316 } 317 318 319 /* publish_devices - null-terminated array of devices supported by this driver. */ 320 const char ** 321 publish_devices() 322 { 323 TRACE_FUNCALLS("> publish_devices()\n"); 324 for (int32 i = 0; gDeviceNames[i]; i++) 325 free(gDeviceNames[i]); 326 327 int32 j = 0; 328 acquire_sem(gDriverLock); 329 for(int32 i = 0; i < DEVICES_COUNT; i++) { 330 if (gSerialDevices[i]) { 331 gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4); 332 if (gDeviceNames[j]) { 333 sprintf(gDeviceNames[j], "%s%ld", sDeviceBaseName, i); 334 j++; 335 } else 336 TRACE_ALWAYS("publish_devices - no memory to allocate device names\n"); 337 } 338 } 339 340 gDeviceNames[j] = NULL; 341 release_sem(gDriverLock); 342 return (const char **)&gDeviceNames[0]; 343 } 344 345 346 /* find_device - return poiter to device hooks structure for a given device */ 347 device_hooks * 348 find_device(const char *name) 349 { 350 static device_hooks deviceHooks = { 351 pc_serial_open, /* -> open entry point */ 352 pc_serial_close, /* -> close entry point */ 353 pc_serial_free, /* -> free cookie */ 354 pc_serial_control, /* -> control entry point */ 355 pc_serial_read, /* -> read entry point */ 356 pc_serial_write, /* -> write entry point */ 357 #if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__) 358 pc_serial_select, /* -> select entry point */ 359 pc_serial_deselect /* -> deselect entry point */ 360 #endif 361 }; 362 363 TRACE_FUNCALLS("> find_device(%s)\n", name); 364 return &deviceHooks; 365 } 366