Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

smokyonion

macrumors newbie
Original poster
Dec 13, 2009
13
0
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.

[Switching to process 1321]
Running…
Modem found with BSD path: /dev/cu.usbserial
Raw device removed.
Program received signal: “EXC_BAD_ACCESS”.
sharedlibrary apply-load-rules all

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");

}
 
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.
 
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:
warning: passing argument 4 of ‘IOServiceAddMatchingNotification’ from incompatible pointer type
 
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. : )
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.