I am just starting out with driver development, and am trying to initialize a device driver through the operating system on start up. The driver is for Windows Embedded CE 6.0.
I have been attempting to have my device send a message through a serial port to my PC whenever it gets initialized.
DWORD MYD_Init(LPCTSTR pContext, LPCVOID lpvBusContext)
{
DWORD dwResult = 1;
RETAILMSG(TRUE, (TEXT("MyDriver: Initializing!\n")));
DEBUGMSG(TRUE, (TEXT("MyDriver: Initializing!\n")));
return dwResult;
} //end MYD_Init
The DLLEntry function is gets called:
BOOL DllEntry(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH:
RETAILMSG(TRUE, (TEXT("MyDriver - Process Attached\n")));
DEBUGMSG(TRUE, (TEXT("MyDriver - Process Attached\n")));
break;
case DLL_PROCESS_DETACH:
RETAILMSG(TRUE, (TEXT("MyDriver - Process Detached\n")));
DEBUGMSG(TRUE, (TEXT("MyDriver - Process Detached\n")));
break;
.......
default:
break;
} //end Switch
return TRUE;
} //end DllEntry
And here are a few lines from the serial port log:
FMD_Init: Blocks reserved for the bootloader/run-time image: 3124
I2C Driver: Intialization Started
MyDriver - Process AttachedMyDriver - Process DetachedTurn on clocks for Device block (in OTG_CTRL)
2589 CUSBFN::IsConfigurationSupportable
2593 CUSBFN::IsEndpointSupportable
2595 CUSBFN::IsEndpointSupportable
Line three shows that the driver gets loaded (I think), but MYD_Init is not called afterward. I'm not sure what I am missing. Maybe a missing reference from another file. My .def file includes the MYD_Init under EXPORTS. The .reg key is within the BuiltIn path: [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\MyDriver]. Not sure if there are any others.
I guess my real question is what are possible things I am missing/forgetting to get the MYD_Init function to get called?
Thanks for any help you guys can provide to help me get this work!
Edit:
I have realized that the CreateFile function is never called and feel this is the reason for my problem. What are some steps that I could take to figure out where this function should be placed so that MYD_Init is not called before DllEntry?
Or maybe the ActivateDevice/ActiveDeviceEx functions? Does the OS automatically call these for the drivers listed under [HKEY_LOCAL_MACHINE\Drivers\BuiltIn], or do they have to be called manually?
Thanks again for any insight that can be provided!
Edit 2:
Here are the registry settings easily readable:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\MyDriver]
"Prefix"="MYD"
"Dll"="myDriver.dll"
"Index"=dword:1
"Flags"=dword:0
Edit 3:
This is the output after trying to open the driver through an application:
USBFN: dwDeviceStatus: 0x11, m_fAttached: Attached
USBFN: dwDeviceStatus: 0x1, m_fAttached: Attached
MyDriver - Process Attached
MyDriver - Process Detached
COM_Open
COM_Open
IOCTL_SERIAL_SET_COMMTIMEOUTS (50,1,50,5,500)
IOCTL_SERIAL_SET_COMMTIMEOUTS (50,1,50,5,500)
IOCTL_SERIAL_SET_COMMTIMEOUTS (-1,0,0,2,500)
Testing Driver
MyDriver - Process Attached
MyDriver - Process Detached
Driver Tested
Line 3 and 4 is the Load attempt during system boot. The last four lines are output when the application to load the driver is run. "Testing Driver" and "Driver Tested" were placed at the beginning and end of the application.
Edit 4: Dumpbin Output
Dump of file myDriver.dll
File Type: DLL
Section contains the following exports for myDriver.dll
00000000 characteristics
501012DA time date stamp Wed Jul 25 10:38:02 2012
0.00 version
1 ordinal base
9 number of functions
9 number of names
ordinal hint RVA name
1 0 000013D4 DllEntry = DllEntry
2 1 00001348 MYD_Close = MYD_Close
3 2 0000130C MYD_Deinit = MYD_Deinit
4 3 000013B8 MYD_IOControl = MYD_IOControl
5 4 000012B8 MYD_Init = MYD_Init
6 5 00001328 MYD_Open = MYD_Open
7 6 00001364 MYD_Read = MYD_Read
8 7 0000139C MYD_Seek = MYD_Seek
9 8 00001380 MYD_Write = MYD_Write
Summary
1000 .data
1000 .pdata
1000 .reloc
1000 .text
Xxx_Init will be called when the driver is loaded. It might be loaded by calling ActivateDevice manually, or by having it automatically loaded by DeviceManager by setting the proper registry settings in [HKLM\Drivers\BuiltIn] (show us what keys you are using).
Xxx_Init is guaranteed to be called after DllEntry because DllEntry gets called when the DLL is loaded by the OS, which has to occur before Device Manager can load it.
CreateFile calls the Xxx_Open method in the driver, which is after all of these actions occur.
So the order of events is:
OS loads the DLL, calling DllEntry
Device Manager loads the driver (either manually or automatic), calling Xxx_Init
App opens the driver by calling CreateFile, which makes Device Manager call Xxx_Open
There are possibly some power management pieces that can fall at 2.5 on the list, but they are optional and likely unrelated to the issue you're seeing.
Typpically I'll use an explicit call to ActivateDevice/DeactivateDevice from a test app when first testing out driver bring-up. It lets you replace the driver DLL without having to reboot the system every time.
So the questions to you are:
If you manually call ActivateDevice from an app, what happens?
What are the registry keys you're currently using to try to get device manager to load the driver?
Related
I have a set of inputs that I want to use to test my program so see what input will hit a break point. I want to create a script to test these inputs one by one and if it hit the break point, print or save the result to a file.
Please let me know if it's possible and if yes, how can I do it. Thank you.
I'm not sure if I have exactly understood what you are asking for. But if I understood correctly, you want to write a program that:
Starts another program
Passes some pre-defined input to the other program
Checks if some breakpoint in the other program is hit
I don't know if this is possible using gdb, but it would be possible to write your own debugger:
Start the program to be tested using fork and one of the exec functions (such as execlp)
Before the exec function call ptrace(PTRACE_TRACEME,0,0,0)
Call waitpid; if exec succeeded, the program will be stopped immediately. The "status code" (second argument) returned by waitpid will be 0x57F (assuming an x86 CPU).
If waitpid returns another exit code, exec failed and you cannot continue.
Use ptrace(PTRACE_PEEKTEXT,...) and ptrace(PTRACE_POKETEXT,...) to modify the program: You place a break point to some address by replacing the instruction at that address by a "breakpoint" instruction (on x86 CPUs: int3 which is byte 0xCC)
This means:
You have to know the addresses (not the line numbers) of the break points and write 0xCC to each address using ptrace().
Because PTRACE_POKETEXT can only modify 4 bytes (x86_32) or 8 bytes (x86_64) at once, you first have to read the old values of these 4 or 8 bytes using PTRACE_PEEKTEXT, modify 1 of 4 or 8 bytes and write all 4 or 8 bytes back.
If your program is not always loading to the same address (due to ASLR etc.) you can read the program counter (using PTRACE_PEEKUSER): It should be the (actual) address of the entry point of the program.
Call ptrace(PTRACE_CONT,pid,0,0) to start the program being tested
Call waitpid to wait for the program to be stopped or to exit
If waitpid returns 0x57F as "status code", you are in the breakpoint. You may now use kill(pid, SIGKILL) to stop your program.
You may use PTRACE_PEEKUSER to check the value of the program counter (rip on x86-64) so you know which breakpoint has been hit. Note that the program counter may be the address of the breakpoint plus 1, so if a breakpoint at address 0x12340000 has been hit, rip may be 0x12340001.
If waitpid returns any other value with the low byte 0x7F, the program caused an exception. You should use kill(pid,SIGKILL) to finally stop it.
Otherwise (if the low byte returned by waitpid is not 0x7F), the program has finished without causing an exception and without hitting any breakpoint.
Here some example code:
int pid, code;
long tmpLong;
pid=fork();
if(!pid)
{
ptrace(PTRACE_TRACEME,0,0,0);
execlp("program_to_be_tested","program_to_be_tested",NULL);
exit(123);
}
waitpid(pid,&code,0);
if(code!=0x57F)
{
/* Starting the program failed ... */
}
else
{
/* Set breakpoints - here assuming x86-64 */
tmpLong=ptrace(PTRACE_PEEKDATA,pid,(void *)(address&~7),0);
((char *)&tmpLong)[address&7]=0xCC;
ptrace(PTRACE_POKEDATA,pid,(void *)(address&~7),(void *)tmpLong);
/* Continue the program */
ptrace(PTRACE_CONT,pid,0,0);
waitpid(pid,&code,0);
if((code&0xFF)!=0x7F)
{
/* Program did not hit a breakpoint
* and did not cause an exception */
}
else if(code==0x57F)
{
/* Breakpoint hit */
kill(pid,SIGKILL);
}
else
{
/* Program caused an exception */
kill(pid,SIGKILL);
}
}
To pass input to your program, you have two possible choices:
Run the debugger multiple times:
echo "Input to be tested" | ./myDebugger
Because your debugger does not read from STDIN, the input will be passed to the program to be tested.
Use pipe and dup2 when creating the child process:
...
pipe(pipes);
pid=fork();
if(!pid)
{
dup2(pipes[0],0);
close(pipes[0]);
close(pipes[1]);
...
}
close(pipes[0]);
write(pipes[1],"Input to be sent to program", ...);
...
I'm writing a shell script that will run on Linux but can operate on files sitting on a mounted partition,
that may or may not have an ext* file-system. It could eg be NTFS, FAT32, or any inode- or non-inode-based system;
that could get further re-mounted on other boxes running non-Linux OSes like Windows or Mac.
Further, my script needs to be able to delete a file on this shared, arbitrarily formatted partition (even while the file's being read from or written to) by a remote process running on Linux, Windows, or Mac boxes
Questions:
Is the ability to be able to delete a file in use a feature of,
a. only the file-system?
b. or, only the OS?
c. or, a combo of both?
(Extension of Q1) Does it matter if the processe doing I/O on the file and the one deleting the file are both local or remote?
Does it matter if the processes doing I/O on the file and the one
deleting the file are both local or remote?
interesting - how remote system can access file (open, read-write data, delete) on windows direct ? really this impossible. we need some agent (LANMan server), running in the local system, which will be by remote commands (send say by Network Redirector) do local operations on file. so from file system view - all operations always local.
Is the ability to be able to delete a file in use a feature of
this is implemented of course by file system driver, but this driver written for concrete OS and based on it rules. while file system data on disk is have common format (as result drive formatted(and written files) in one OS, can be read from another OS) - how file system driver process requests, open, read, write, delete files - this is OS specific. different for different OS. based on it rules. so data format on disk is common and only depend from file system. but how this data read/write/delete - already os specific.
in windows we have next rules for delete files:
Normally a file marked for deletion is not actually deleted until all
open handles for the file have been closed and the link count for the
file is zero. When marking a file for deletion using
FILE_DISPOSITION_POSIX_SEMANTICS, the link gets removed from the
visible namespace as soon as the POSIX delete handle has been closed,
but the file’s data streams remain accessible by other existing
handles until the last handle has been closed.
so in general file will be not deleted, until the last handle to it will be closed. file became not accessible after we try delete it - can not more open it (we get error A non close operation has been requested of a file object with a delete pending. if try do this, after file marked to delete). but if file was already opened - we can still work with it by this handle. also file can not be deleted if section exist on file - will be error An attempt has been made to remove a file or directory that cannot be deleted.
begin from win10 redstone1 build exist FILE_DISPOSITION_POSIX_SEMANTICS flag which let removed file name from the visible namespace when delete handle has been closed, but the file’s data streams remain accessible by other existing handles until the last handle has been closed
windows code test demo: (FILE_DISPOSITION_POSIX_SEMANTICS supported by ntfs begin only from _WIN32_WINNT_WIN10_RS1. FileDispositionInfoEx information class also supported begin from _WIN32_WINNT_WIN10_RS1 only. in previous build we simply got not implemented error)
void print_error(PCSTR name)
{
PWSTR sz;
NTSTATUS status = RtlGetLastNtStatus();
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle(L"ntdll"), status, 0, (PWSTR)&sz, 0, 0))
{
DbgPrint("%s=%x\n%S\n", name, status, sz);
LocalFree(sz);
}
}
HANDLE OpenFile(PCWSTR lpFileName, DWORD dwDesiredAccess)
{
HANDLE hFile = CreateFileW(lpFileName, dwDesiredAccess, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
print_error("OpenFile");
return 0;
}
return hFile;
}
void ReadTest(HANDLE hFile)
{
if (hFile)
{
ULONG dwBytes;
if (ReadFile(hFile, &dwBytes, sizeof(dwBytes), &dwBytes, 0))
{
DbgPrint("ReadFile=OK\n");
}
else
{
print_error("ReadFile");
}
}
}
void DeleteTest(PCWSTR lpFileName)
{
HANDLE hFile1, hFile2, hFile3;
if (hFile1 = OpenFile(lpFileName, DELETE))
{
hFile2 = OpenFile(lpFileName, FILE_GENERIC_READ);
FILE_DISPOSITION_INFO_EX fdi = { FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS };
if (!SetFileInformationByHandle(hFile1, FileDispositionInfoEx, &fdi, sizeof(fdi)))
{
print_error("SetFileInformationByHandle");
}
// file already not accessible here (open must fail) but visible
if (hFile3 = OpenFile(lpFileName, FILE_GENERIC_READ))
{
CloseHandle(hFile3);
}
ReadTest(hFile2);
// win10 rs1: file removed from the visible namespace here
CloseHandle(hFile1);
// are file still visible ?
if (hFile3 = OpenFile(lpFileName, FILE_GENERIC_READ))
{
CloseHandle(hFile3);
}
// are possible create new file with this name &
hFile3 = CreateFileW(lpFileName, DELETE,
FILE_SHARE_VALID_FLAGS, 0, CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE, 0);
if (hFile3 == INVALID_HANDLE_VALUE)
{
print_error("CreateFile");
}
else
{
CloseHandle(hFile3);
DbgPrint("CreateFile OK\n");
}
ReadTest(hFile2);
if (hFile2)
{
CloseHandle(hFile2);
}
}
}
and output
OpenFile=c0000056
A non close operation has been requested of a file object with a delete pending.
ReadFile=OK
OpenFile=c0000034
Object Name not found.
CreateFile OK
ReadFile=OK
it depends how do you define filesystem and OS. Generally, I understand under filesystem the way how the data storage is organized on a device. OS is then responsible for the I/O of data and files. Particularly, if your script wants to delete a file it calls some utility like rm and provides name of the file. This utility is a program which makes an appropriate system call. This system call is a part of operating system which is executed in the privileged mode. It realizes what and how to do (e.g. which drivers should be used to mark HDD blocks as free on a particular drive or maybe some remote procedure should be called or samba server involved etc.)
So, to give an answer to your question 1, I would tend to the answer b.
Normally printk does not print any messages before console_init which is present in start_kernel. But with early_printk enabled, printk starts printing messages before console initialization. Now how does this behaviour of printk change since I am still using printk function to print debug messages and not early_printk function. How is this mapping done?
It's not really a mapping. When early_printk is enabled, the same printk() is used as before, just new boot console is being registered in that case, and printk() uses it on early boot stages.
Look at arch/arm/kernel/early_printk.c. You can see that:
new console being registered with register_console() function
that console has CON_BOOT flag, so it's unregistered automatically as soon as real console is registered
printing happens via early_write() function, which in turn uses printch() function, which implemented for each platform separately
Where in kernel source the early_console is disabled after kernel console initialization?
It's done in register_console() function:
if (bcon &&
((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
!keep_bootcon) {
/* We need to iterate through all boot consoles, to make
* sure we print everything out, before we unregister them.
*/
for_each_console(bcon)
if (bcon->flags & CON_BOOT)
unregister_console(bcon);
}
All boot consoles are disabled by unregister_console() function in code above (when real console is being registered).
And where is the real console getting registered?
Real consoles use the same method for registration -- register_console() function. For example:
from my board's defconfig file (arch/arm/configs/omap2plus_defconfig) I can see that my board is using CONFIG_SERIAL_8250 as real console
we can search where register_console() is executed in my serial driver; it's done in univ8250_console_init() function
Is there any way to keep boot consoles up after console initialization and disable real console?
Boot consoles are automatically unregistered only when real console is registered. Following this logic, you just need to disable the real console in order to keep boot console intact.
So what you need to do, is to find out which exactly driver is used for real console in your case. You can do that looking into your .config file, or the *_defconfig file for your board. Once you located it, just disable that driver in configuration and rebuild the kernel.
If after doing so you keep observing the registering of some real console, you need to add some debug printings to register_console(), to figure out what driver is being registered, and then disable it in your configuration.
I am currently working on a C program running on a Raspberry Pi 3 (Linux Ubuntu) that is intended to provide a web page interface for configuring networking on an embedded system.
The code is being developed using Code::Blocks with the GDB debugger. I'm using microhttpd for the web server and that, plus the various web pages, are all working great. I'm now working on the USB Serial link to the embedded system using information in "Serial Programming Guide for POSIX Operating Systems".
The code below is responsible for opening the USB Serial link to the target system and seems to work fine - once. If I close the program and restart it (either standalone on the command line or from within Code::Blocks) the second time microhttpd is hosed - browser windows will no longer connect. Further, from within Code::Blocks the debugger is also hosed - once the program is started it cannot be paused or stopped. The only way is to kill it by closing the project.
The problem is clearly within the function since I can comment out the call to it and everything works as it did previously. Unfortunately, once the problem happens the only solution seems to be to reboot the Pi.
I've done things like this before using a scripting language (Tcl) but this time around I'm looking for a performance boost from a non-interpreted language since the Pi will also be running a high bandwidth data logging program through a similar USB serial interface.
The code is shown below:
/******************************************************************************/
/* This function scans through the list of USB Serial ports and tries to */
/* establish communication with the target system. */
/******************************************************************************/
void tapCommInit(void) {
char line[128];
char port[15]; // this is always of the form "/dev/TTYACMn"
char *ptr;
FILE *ifd;
struct termios options;
uint8_t msgOut[3], msgIn[4];
msgOut[0] = REQ_ID; // now prepare the message to send
msgOut[1] = 0; // no data so length is zero
msgOut[2] = 0;
/**************************************************************************/
/* First, get the list of USB Serial ports. */
/**************************************************************************/
system("ls -l /dev/serial/by-path > usbSerial\n"); // get current port list
ifd = fopen("usbSerial", "r");
logIt(fprintf(lfd, "serial ports: \n"));
/**************************************************************************/
/* The main loop iterates through the file looking for lines containing */
/* "tty" which should be a valid USB Serial port. The port is configured */
/* in raw mode as 8N1 and an ID request command is sent, which has no */
/* data. If a response is received it's checked to see if the returned */
/* ID is a match. If not, the port is closed and we keep looking. If a */
/* match is found, tapState is set to "UP" and the function returns. If */
/* no match is found, tapState is left in the initial "DOWN" state. */
/**************************************************************************/
while(1) {
if (fgets(line, 127, ifd) == NULL) { // end of file?
break; // yes - break out and return
}
ptr = strstr(line, "tty"); // make sure the line contains a valid entry
if (ptr == NULL) {
continue; // nothing to process on this line
}
strcpy(port, "/dev/"); // create a correct pathname
strcat(port, ptr); // append the "ttyACMn" part of the line
port[strlen(port)-1] = 0; // the last character is a newline - remove it
logIt(fprintf(lfd," %s\n", port)); // we have a port to process now
cfd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); // cfd is a global int
if (cfd == -1) {
logIt(fprintf(lfd, "Could not open port: %s\n", port));
continue; // keep going with the next one (if any)
}
fcntl(cfd, F_SETFL, 0); // blocking mode
tcgetattr(cfd, &options); // get the current port settings
options.c_cflag |= (CLOCAL | CREAD); // ena receiver, ignore modem lines
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw, no echo
options.c_oflag &= ~OPOST; // no special output processing
options.c_cc[VMIN] = 0; // minimum number of raw read characters
options.c_cc[VTIME] = 10; // timeout in deciseconds (1 second timeout)
tcsetattr(cfd, TCSANOW, &options); // set options right now
cfsetispeed(&options, B115200); // input baud rate
cfsetospeed(&options, B115200); // output baud rate
options.c_cflag &= ~(CSIZE | PARENB | // clear size bits, no parity
CSTOPB | CRTSCTS); // 1 stop bit, no hw flow control
options.c_cflag |= CS8; // now set size: 8-bit characters
options.c_cflag &= ~(IXON | IXOFF | IXANY); // no sw flow control
if (write(cfd, msgOut, 3) < 3) {
logIt(fprintf(lfd, "Sending of output message failed\n"));
close(cfd);
continue;
}
if (read(cfd, msgIn, 4) != 4) {
logIt(fprintf(lfd, "Didn't get expected amount of return data\n"));
close(cfd);
continue;
}
if (msgIn[3] != HOST_ID) {
logIt(fprintf(lfd, "Got the wrong HOST_ID response\n"));
close(cfd);
continue;
}
logIt(fprintf(lfd, "Port found - communication established\n"));
tapState = UP;
break; // we're done - break out of the loop
}
fclose(ifd); // close and remove the file we created
remove("usbSerial");
}
from within Code::Blocks the debugger is also hosed - once the program is started it cannot be paused or stopped
It is far more likely that you do not understand your tools than that you have created an unkillable program.
It's easy enough to figure this out: divide and conquer. You've got a whole pile of unrelated components here. Start separating them and find out which pieces work fine in isolation and which continue to behave badly when disconnected from everything else. Then you'll have your culprit.
Specifically here, that means try running your program outside the IDE, then under command line gdb instead of GDB via the IDE.
Also, it should be possible to run your program without starting the web server piece, so that you can run the serial part of the app in isolation. This is not only good for debugging by minimizing confounding variables, it also encourages a loosely-coupled program design, which is a good thing in its own right.
In the end, you may find that the thing keeping your program from stopping is the web framework, Code::Blocks, or the way GDB operates on the Pi under Code::Blocks, rather than anything to do with the USB to serial adapter.
once the problem happens the only solution seems to be to reboot the Pi
If your program is still running in the background, then of course your next instance will fail if it tries to open the same USB port.
Don't guess, find out:
$ sudo lsof | grep ttyACM
or:
$ lsof -p $(pidof myprogram)
(Substitute pgrep if your system doesn't have pidof.)
I've done things like this before using a scripting language (Tcl) but this time around I'm looking for a performance boost from a non-interpreted language
Your serial port is running at 115,200 bps. Divide that by 10 to account for the stop and start bits, then flip the fraction to get seconds per byte, and you come to 87 microseconds per byte. And you only achieve that when the serial port is running flat-out, sending or receiving 11,500 bytes per second. Wanna take a guess at how many lines of code Tcl can interpret in 87 microseconds? Tcl isn't super-fast, but 87 microseconds is an eternity even in Tcl land.
Then on the other side of the connection, you have HTTP and a [W]LAN, likely adding another hundred milliseconds or so of delay per transaction.
Your need for speed is an illusion.
Now come back and talk to me again when you need to talk to 100 of these asynchronously, and then maybe we can start to justify C over Tcl.
(And I say this as one whose day job involves maintaining a large C++ program that does a lot of serial and network I/O.)
Now lets get to the many problems with this code:
system("ls -l /dev/serial/by-path > usbSerial\n"); // get current port list
ifd = fopen("usbSerial", "r");
Don't use a temporary where a pipe will suffice; use popen() here instead.
while(1) {
This is simply wrong. Say while (!feof(ifd)) { here, else you will attempt to read past the end of the file.
This, plus the next error, is likely the key to your major symptoms.
if (fgets(line, 127, ifd) == NULL) {
break;
There are several problems here:
You're assuming things about the meaning of the return value that do not follow from the documentation. The Linux fopen(3) man page isn't super clear on this; the BSD version is better:
The fgets() and gets() functions do not distinguish between end-of-file and error, and callers must use feof(3) and ferror(3) to determine which occurred.
Because fgets() is Standard C, and not Linux- or BSD-specific, it is generally safe to consult other systems' manual pages. Even better, consult a good generic C reference, such as Harbison & Steele. (I found that much more useful than K&R back when I was doing more pure C than C++.)
Bottom line, simply checking for NULL doesn't tell you everything you need to know here.
Secondarily, the hard-coded 127 constant is a code bomb waiting to go off, should you ever shrink the size of the line buffer. Say sizeof(line) here.
(No, not sizeof(line) - 1: fgets() leaves space for the trailing null character when reading. Again, RTFM carefully.)
The break is also a problem, but we'll have to get further down in the code to see why.
Moving on:
strcat(port, ptr); // append the "ttyACMn" part of the line
Two problems here:
You're blindly assuming that strlen(ptr) <= sizeof(port) - 6. Use strncat(3) instead.
(The prior line's strcpy() (as opposed to strncpy()) is justifiable because you're copying a string literal, so you can see that you're not overrunning the buffer, but you should get into the habit of pretending that the old C string functions that don't check lengths don't even exist. Some compilers will actually issue warnings when you use them, if you crank the warning level up.)
Or, better, give up on C strings, and start using std::string. I can see that you're trying to stick to C, but there really are things in C++ that are worth using, even if you mostly use C. C++'s automatic memory management facilities (not just string, but also auto_ptr/unique_ptr and more) fall into this category.
Plus, C++ strings operate more like Tcl strings, so you'll probably be more comfortable with them.
Factual assertions in comments must always be true, or they are likely mislead you later, potentially hazardously so. Your particular USB to serial adapter may use /dev/ttyACMx, but not all do. There's another common USB device class used by some serial-to-USB adapters that causes them to show up under Linux as ttyUSBx. More generally, a future change may change the device name in some other way; you might port to BSD, for example, and now your USB to serial device is called /dev/cu.usbserial, blowing your 15-byte port buffer. Don't assume.
Even with the BSD case aside, your port buffer should not be smaller than your line buffer, since you are concatenating the latter onto the former. At minimum, sizeof(port) should be sizeof(line) + strlen("/dev/"), just in case. If that seems excessive, it is only because 128 bytes for the line buffer is unnecessarily large. (Not that I'm trying to twist your arm to change it. RAM is cheap; programmer debugging time is expensive.)
Next:
fcntl(cfd, F_SETFL, 0); // blocking mode
File handles are blocking by default in Unix. You have to ask for a nonblocking file handle. Anyway, blasting all the flags is bad style; you don't know what other flags you're changing here. Proper style is to get, modify, then set, much like the way you're doing with tcsetattr():
int flags;
fcntl(cfd, F_GETFL, &flags);
flags &= ~O_NONBLOCK;
fcntl(cfd, F_SETFL, flags);
Well, you're kind of using tcsetattr() correctly:
tcsetattr(cfd, TCSANOW, &options);
...followed by further modifications to options without a second call to tcsetattr(). Oops!
You weren't under the impression that modifications to the options structure affect the serial port immediately, were you?
if (write(cfd, msgOut, 3) < 3) {
logIt(fprintf(lfd, "Sending of output message failed\n"));
close(cfd);
continue;
}
Piles of wrong here:
You're collapsing the short-write and error cases. Handle them separately:
int bytes = write(cfd, msgOut, 3);
if (bytes == 0) {
// can't happen with USB, but you may later change to a
// serial-to-Ethernet bridge (e.g. Digi One SP), and then
// it *can* happen under TCP.
//
// complain, close, etc.
}
else if (bytes < 0) {
// plain failure case; could collapse this with the == 0 case
// close, etc
}
else if (bytes < 3) {
// short write case
}
else {
// success case
}
You aren't logging errno or its string equivalent, so when (!) you get an error, you won't know which error:
logIt(fprintf(lfd, "Sending of output message failed: %s (code %d)\n",
strerror(errno), errno));
Modify to taste. Just realize that write(2), like most other Unix system calls, has a whole bunch of possible error codes. You probably don't want to handle all of them the same way. (e.g. EINTR)
After closing the FD, you're leaving it set to a valid FD value, so that on EOF after reading one line, you leave the function with a valid but closed FD value! (This is the problem with break above: it can implicitly return a closed FD to its caller.) Say cfd = -1 after every close(cfd) call.
Everything written above about write() also applies to the following read() call, but also:
if (read(cfd, msgIn, 4) != 4) {
There's nothing in POSIX that tells you that if the serial device sends 4 bytes that you will get all 4 bytes in a single read(), even with a blocking FD. You are especially unlikely to get more than one byte per read() with slow serial ports, simply because your program is lightning fast compared to the serial port. You need to call read() in a loop here, exiting only on error or completion.
And just in case it isn't obvious:
remove("usbSerial");
You don't need that if you switch to popen() above. Don't scatter temporary working files around the file system where a pipe will do.
I am a newbie.
I want to develop device drivers on Windows CE 7.
I use Adeneo BSP on TI AM33xx Development Board
I want to drive a SPI Flash so I should use SPI driver.
I have written my driver and when I debug it, all functions calling correctly. But when I call the
CreateFile( _T("SPI1:"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
deviceHandle always return invalid value. So when I debug the low level SPI driver, I seen , SPI_Init() stream function never called.
There is this tines in my platform.reg file
;===============================================================================
; MCSPI driver
#include "$(_PLATFORMROOT)\AM33x_BSP\SRC\DRIVERS\MCSPI\mcspi.reg"
And
My mcspi.reg file is
;-- SPI Driver -----------------------------------------------------------------
IF BSP_AM33X_MCSPI1
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SPI1]
"Prefix"="SPI"
"Dll"="am33x_mcspi.dll"
"Index"=dword:1
"Port"=dword:1
"Order"=dword:8
"MemBase"=multi_sz:"48030000"
"MemLen"=multi_sz:"00000100"
"Timeout"=dword:300
"PowerFlags"=dword:00000103 ; send pre/post device state changes
; PMCLASS_PMEXT_GUID
"IClass"=multi_sz:"{0AE2066F-89A2-4D70-8FC2-29AEFA68413C}"
ENDIF
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SPI1] this lines say to us, "SPI1 driver will be loaded at startup", isnt it ??? But spi driver SPI_Init function never loaded at starttup. I havent seen any extra setting in Microsoft White Paper.
And additional,
I have tried
ActivateDeviceEx(TEXT("Drivers\\BuiltIn\\SPI1"), NULL, 0, NULL);
and
LoadLibrary(TEXT("am33x_mcspi.dll"));
functions but result is same.
Is there any extra setting for Driver Loading?
Thanks
Murat.
Make sure your driver is compiled to your flat release directory. Then, add your driver to your platform.bib file. something like this:
am33x_mcspi.dll $(_FLATRELEASEDIR)\am33x_mcspi.dll NK SH