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