EXC_BAD_ACCESS signal received a RS232 communication program

Discussion in 'Mac Programming' started by smokyonion, Dec 30, 2009.

  1. smokyonion macrumors newbie

    Joined:
    Dec 13, 2009
    #1
    Dear All:

    I am writing a small program to deal with a RS232 device (serial port)
    and I refer to the SerialPortExample and USBNotificationExample on the Apple Developer Website.

    I have established my own dictionary to lookup my device and my goal is to create the hot plug in/out function.

    ----Question----
    So far I can get the device path while I plugin my device and the message "Device removed" while I plug it out.

    But when I plug the device in again the console will show the below message in the Quote Section and the the debugger will stop at the *bsdPath = '\0';


    I try to read the Apple Online document, but I cannot find solution for this...please help. any reply will be highly appreciated.

    this is the complete code .
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <errno.h>
    #include <paths.h>
    #include <termios.h>
    #include <sysexits.h>
    #include <sys/param.h>
    #include <sys/select.h>
    #include <sys/time.h>
    #include <time.h>
    #include <AvailabilityMacros.h>
    
    #include <CoreFoundation/CoreFoundation.h>
    
    #include <IOKit/IOKitLib.h>
    #include <IOKit/IOCFPlugIn.h>
    #include <IOKit/serial/IOSerialKeys.h>
    #include <IOKit/serial/ioss.h>
    #include <IOKit/IOBSD.h>
    #include <mach/mach.h>
    
    // globals
    static IONotificationPortRef	        gNotifyPort;
    static io_iterator_t			gRawAddedIter;
    static io_iterator_t			gRawRemovedIter;
    
    void SignalHandler(int sigraised);
    void RawDeviceAdded(void *refCon, io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize);
    void RawDeviceRemoved(void *refCon, io_iterator_t serialPortIterator);
    
    void SignalHandler(int sigraised)
    {
        printf("\nInterrupted\n");
    	
        // Clean up here
        IONotificationPortDestroy(gNotifyPort);
    	
        if (gRawAddedIter) 
        {
            IOObjectRelease(gRawAddedIter);
            gRawAddedIter = 0;
        }
    	
        if (gRawRemovedIter) 
        {
            IOObjectRelease(gRawRemovedIter);
            gRawRemovedIter = 0;
        }
        
        if (gBulkTestAddedIter) 
        {
            IOObjectRelease(gBulkTestAddedIter);
            gBulkTestAddedIter = 0;
        }
    	
        if (gBulkTestRemovedIter) 
        {
            IOObjectRelease(gBulkTestRemovedIter);
            gBulkTestRemovedIter = 0;
        }
    	
        // exit(0) should not be called from a signal handler.  Use _exit(0) instead
        //
        _exit(0);
    }
    
    void RawDeviceAdded(void *refCon, io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize)
    {
        io_object_t		serialDevice; // mach_port_t
    	kern_return_t	kernResult = KERN_FAILURE;
        Boolean			deviceFound = false;
        
        // Initialize the returned path
        *bsdPath = '\0';  // [B]debugger will stop at here[/B]
    	
        while ( (serialDevice = IOIteratorNext(serialPortIterator)) )
        {
    		CFTypeRef	bsdPathAsCFString;
    		
    		// Get the callout device's path (/dev/cu.xxxxx). The callout device should almost always be
    		// used: kIOCalloutDeviceKey
    		// the dialin device (/dev/tty.xxxxx), kIODialinDeviceKey, would be used when monitoring a serial port for
    		// incoming calls, e.g. a fax listener.
    		
    		bsdPathAsCFString = IORegistryEntryCreateCFProperty(serialDevice,
                                                                CFSTR(kIOCalloutDeviceKey),
                                                                kCFAllocatorDefault,
                                                                0);
    		if (bsdPathAsCFString)
            {
                Boolean result;
                
                // Convert the path from a CFString to a C (NUL-terminated) string for use
    			// with the POSIX open() call.
    			
    			result = CFStringGetCString(bsdPathAsCFString,
                                            bsdPath,
                                            maxPathSize, 
                                            kCFStringEncodingUTF8);
                CFRelease(bsdPathAsCFString);
                
                if (result)
    			{
    				// check the bsdPath if it is an empty array
    				if (!bsdPath[0])
    				{
    					printf("No modem port found.\n");
    					//return EX_UNAVAILABLE;
    				}
    				else
    				{
    					printf("Modem found with BSD path: %s", bsdPath);
    					deviceFound = true;
    					kernResult = KERN_SUCCESS;
    				}
                }
            }
    		
            printf("\n");
    		
            // Release the io_object_t now that we are done with it.	
    		(void) IOObjectRelease(serialDevice);
        }
    }
    
    void RawDeviceRemoved(void *refCon, io_iterator_t serialPortIterator)
    {
        kern_return_t	kr;
        io_object_t	obj;
        
        while ( (obj = IOIteratorNext(serialPortIterator)) )
        {		
            printf("Raw device removed.\n");
            kr = IOObjectRelease(obj);
        }
    }
    
    int main (int argc, const char * argv[])
    {
    
    	
    	kern_return_t	kernResult; // on PowerPC this is an int (4 bytes)
    	/*
    	 *	error number layout as follows (see mach/error.h):
    	 *
    	 *	hi		 		       lo
    	 *	| system(6) | subsystem(12) | code(14) |
    	 */
    	
    	//----------Handler Secrion---------
    	mach_port_t				masterPort;
    	sig_t					oldHandler;
    	
    	// Set up a signal handler so we can clean up when we're interrupted from the command line
        // Otherwise we stay in our run loop forever.
        oldHandler = signal(SIGINT, SignalHandler);
        if (oldHandler == SIG_ERR)
            printf("Could not establish new signal handler");
    	
    	// first create a master_port for my task
        kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
        if (kernResult || !masterPort)
        {
            printf("ERR: Couldn't create a master IOKit Port(%08x)\n", kernResult);
            return -1;
        }
    	
    	//---------Create Dictionary,Find Device-----------
    	CFMutableDictionaryRef	devicesToMatchDictionary;
    	
    	/*! @function IOServiceMatching
    	 @abstract Create a matching dictionary that specifies an IOService class match.
    	 @discussion A very common matching criteria for IOService is based on its class. IOServiceMatching will create a matching dictionary that specifies any IOService of a class, or its subclasses. The class is specified by C-string name.
    	 @param name The class name, as a const C-string. Class matching is successful on IOService's of this class or any subclass.
    	 @result The matching dictionary created, is returned on success, or zero on failure. The dictionary is commonly passed to IOServiceGetMatchingServices or IOServiceAddNotification which will consume a reference, otherwise it should be released with CFRelease by the caller. */
    	
        // Serial devices are instances of class IOSerialBSDClient
    	// see "Accessing Hardwares From Applications.pdf", 
    	// page 33 - Setting Up a Matching Dictionary to Find Device Files
    	devicesToMatchDictionary = IOServiceMatching(kIOSerialBSDServiceValue); // IOSerialBSDClient
    	
    	if (devicesToMatchDictionary == NULL)
        {
            printf("IOServiceMatching returned a NULL dictionary.\n");
    		printf("Can't create a Serial Port Device matching dictionary\n");
            mach_port_deallocate(mach_task_self(), masterPort);
            return -1;
        }
        else 
    	{
    		/*!
    		 @function CFDictionarySetValue
    		 Sets the value of the key in the dictionary.
    		 @param theDict The dictionary to which the value is to be set. If this
    		 parameter is not a valid mutable CFDictionary, the behavior is
    		 undefined. If the dictionary is a fixed-capacity dictionary and
    		 it is full before this operation, and the key does not exist in
    		 the dictionary, the behavior is undefined.
    		 @param key The key of the value to set into the dictionary. If a key 
    		 which matches this key is already present in the dictionary, only
    		 the value is changed ("add if absent, replace if present"). If
    		 no key matches the given key, the key-value pair is added to the
    		 dictionary. If added, the key is retained by the dictionary,
    		 using the retain callback provided
    		 when the dictionary was created. If the key is not of the sort
    		 expected by the key retain callback, the behavior is undefined.
    		 @param value The value to add to or replace into the dictionary. The value
    		 is retained by the dictionary using the retain callback provided
    		 when the dictionary was created, and the previous value if any is
    		 released. If the value is not of the sort expected by the
    		 retain or release callbacks, the behavior is undefined.
    		 */
    		CFDictionarySetValue(devicesToMatchDictionary,
    							 CFSTR(kIOSerialBSDTypeKey),	// IOSerialBSDClientType
    							 CFSTR(kIOSerialBSDRS232Type)); // IORS232SerialStream
    		
    		// we are going to set one more value into dictionary
    		CFDictionarySetValue(devicesToMatchDictionary,
    							 CFSTR(kIOTTYBaseNameKey),	// IOTTYBaseName
    							 CFSTR("usbserial"));		// usbserial
    		
            
    		// Each serial device object has a property with key
            // kIOSerialBSDTypeKey and a value that is one of kIOSerialBSDAllTypes,
            // kIOSerialBSDModemType, or kIOSerialBSDRS232Type. You can experiment with the
            // matching by changing the last parameter in the above call to CFDictionarySetValue.
        }
    	
    	
    	// Create a notification port and add its run loop event source to our run loop
        // This is how async notifications get set up.
        gNotifyPort = IONotificationPortCreate(masterPort);
    	
    	CFRunLoopSourceRef		runLoopSource;
    	
    	// use the gNotifyPort (notification object) received from IONotificationPortCreate
        runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);
        
        CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
    	
    	// Retain additional references because we use this same dictionary with four calls to 
        // IOServiceAddMatchingNotification, each of which consumes one reference.
        devicesToMatchDictionary = (CFMutableDictionaryRef) CFRetain( devicesToMatchDictionary ); 
        devicesToMatchDictionary = (CFMutableDictionaryRef) CFRetain( devicesToMatchDictionary ); 
        devicesToMatchDictionary = (CFMutableDictionaryRef) CFRetain( devicesToMatchDictionary );
    	
    	// Now set up two notifications, one to be called when a raw device is first matched by I/O Kit, and the other to be
        // called when the device is terminated.
        kernResult = IOServiceAddMatchingNotification(gNotifyPort,					// the notification object you received from IONotificationPortCreate
    												  kIOFirstMatchNotification,	// A constant defining the type of event you want notification of, such as device registration or termination (these constants are defined in IOKitKeys.h in the i/O Kit Framework)
    												  devicesToMatchDictionary,		// the Core Foundation matching dictionary you've created
    												  RawDeviceAdded,				// the function you want called
    												  NULL,							// An optional reference constant for your callback function's use
    												  &gRawAddedIter);				// An io_iterator_t object to access the list of matching devices
    	
    	int		fileDescriptor;
    	char	bsdPath[MAXPATHLEN];
    	
        RawDeviceAdded(NULL, gRawAddedIter, bsdPath, sizeof(bsdPath)); // Iterate once to get already-present devices and arm the notification
    	
    	kernResult = IOServiceAddMatchingNotification(gNotifyPort,
    												  kIOTerminatedNotification,
    												  devicesToMatchDictionary,
    												  RawDeviceRemoved,
    												  NULL,
    												  &gRawRemovedIter);
    	
    	RawDeviceRemoved(NULL, gRawRemovedIter);	// Iterate once to arm the notification
    	
    	// Now done with the master_port
        mach_port_deallocate(mach_task_self(), masterPort);
        masterPort = 0;
    	
        // Start the run loop. Now we'll receive notifications.
        CFRunLoopRun();
    	
        // We should never get here
        return 0;
    
    //	// Open specified serial port
    //	fileDescriptor = OTOpenSerialPort(bsdPath);
    //    if (-1 == fileDescriptor)
    //    {
    //        return EX_IOERR;
    //    }
    //	
    //	
    //	if (OTInitializeDevice(fileDescriptor))
    //    {
    //        printf("Modem initialized successfully.\n");
    //    }
    //    else {
    //        printf("Could not initialize modem.\n");
    //    }
    	
    //CloseSerialPort(fileDescriptor);
    //printf("Modem port closed.\n");
    
    }
    
    
     
  2. HiRez macrumors 603

    HiRez

    Joined:
    Jan 6, 2004
    Location:
    Western US
    #2
    Hard to say, but since the last message received was "Raw device removed.", I would look at the line

    Code:
    kr = IOObjectRelease(obj);
    You may be trying to deallocate memory that has already been deallocated (maybe you have a bad pointer?). I would start by putting another printf after that line and see if the program is getting past that. If so then see where that gets called from and move up the chain until you find where the error is. Or better yet run with the debugger and find the place when it goes bad.
     
  3. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #3
    I think the problem is the function signature for RawDeviceAdded. IOServiceAddMatchingNotification()'s 4th argument is an IOServiceMatchingCallback, which is defined as
    Code:
    typedef void ( *IOServiceMatchingCallback)( 
        void *refcon, 
        io_iterator_t iterator );
    However, your function is
    Code:
    void RawDeviceAdded(void *refCon, io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize);
    Assuming C even lets you do this (anyone else know?), bsdPath and maxPathSize will still be NULL/0, but you're assuming bsdPath is not NULL and still accessing it, thus the crash.

    You should at least try this to prevent the crash:
    Code:
    if (bsdPath)
        *bsdPath = '\0';
    
    Or preferred, refactor your code.


    Edit: you should have gotten this warning:
     
  4. smokyonion thread starter macrumors newbie

    Joined:
    Dec 13, 2009
    #4
    Dear HiRez and kainjow:

    Thank you so much for you replies.

    I have checked the IOServiceAddMatchingNotification()'s 4th argument

    I think it's the cause of this problem.

    When I was using the old style I declared, there was a warning message as kainjow said, but the program can still run

    so I changed it as follows

    Code:
    void RawDeviceAdded(void *refCon, io_iterator_t serialPortIterator)
    
    and then put the bsdPath
    Code:
    char bsdPath[MAXPATHLEN];
    
    in the RawDeviceAdded;

    it does solve my problem, thank you so much guys, you two rock.

    By the way, there is also a mistake.
    I did not assign a value to the 3rd argument of CFStringGetCString - "maxPathSize".

    I have to assign it the value - the path size of bsdPath:
    Code:
    CFIndex maxPathSize = sizeof(bsdPath);
    
    If I did not, the function cannot show the path in the console at each time's build and run.

    Thank you. : )
     

Share This Page